Как обрабатывать большие числа в шейдерах WebGL GLSL?

Как я могу обрабатывать большие числа, такие как приведенное ниже, в GLSL?

Я предоставляю шейдеру Date.now() в качестве униформы, который описывается как:

Метод Date.now() возвращает количество миллисекунд, прошедших с 00:00:00 UTC 1 января 1970 года. — MDN

Например, 1514678400000 — последний день года.

Передача этого значения моему ShaderMaterial приводит к тому, что ничего особенного не происходит, если только я не уменьшу значение на много.

В частности, это та часть, где поведение, кажется, расходится с моим ожиданием отображения последних 2500 мс в значение в диапазоне от 0 до 1:

JavaScript: ( Date.now() % 2500 ) / 2500

GLSL: mod( time, 2500.0 ) / 2500.0

Вместо этого я бы предпочел выполнять эти вычисления на графическом процессоре, но не уверен, как мне к этому подойти?

Ниже приведена небольшая сцена, иллюстрирующая проблему:

const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 )
const renderer = new THREE.WebGLRenderer()

renderer.setSize( window.innerWidth, window.innerHeight )
camera.position.z = 0.5

document.body.appendChild( renderer.domElement )

const checkbox = document.getElementById( "toggle" )

const geo = new THREE.PlaneGeometry( 1, 1 )
const mat = new THREE.ShaderMaterial({
  vertexShader: `
    void main() {
      gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
    }
  `,
  fragmentShader: `
    uniform bool check;
    uniform float time;
    
    const float TAU = 6.2831;
    
    void main() {
      float fColor;
      
      check 
        ? fColor = ( sin( ( time * TAU ) ) + 1.0 ) / 2.0
        : fColor = ( sin( ( ( mod( time, 2500.0 ) / 2500.0 ) * TAU ) ) + 1.0 ) / 2.0;
        
      gl_FragColor = vec4( 1.0, fColor, 1.0, 1.0 );
    }
  `,
  uniforms: {
    "check": { value: false },
    "time": { value: 1.0 },
  },
})

const plane = new THREE.Mesh( geo, mat )
scene.add( plane )

const animate = function() {
  requestAnimationFrame( animate )

  if ( checkbox.checked ) {
    plane.material.uniforms.check.value = true
    plane.material.uniforms.time.value = ( Date.now() % 2500 ) / 2500
  } else {
    plane.material.uniforms.check.value = false
    plane.material.uniforms.time.value = Date.now()
  }

  renderer.render( scene, camera )
}

animate()
body {
  margin: 0;
}

canvas {
  width: 100%;
  height: 100%;
}

#config {
  position: absolute;
  color: #fff;
  cursor: pointer;
  user-select: none;
  font: bold 1em/1.5 sans-serif;
  padding: 1em;
}

#config label,
#config input {
  cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/89/three.js"></script>
<div id="config">
  <input id="toggle" type="checkbox">
  <label for="toggle">Use JavaScript</label>
</div>


person Jason    schedule 23.12.2017    source источник


Ответы (1)


Я добавляю шейдер с Date.now()

Он производит непредсказуемые вещи в шейдерах, особенно когда вы используете тригонометрические функции, такие как sin(), cos() и так далее. Причина в том, что вы передаете миллисекунды как большое целое число. Вот почему вы должны разделить его на 1000, чтобы сделать их числом с плавающей запятой, то есть преобразовать миллисекунды в секунды.

Итак, деление — это один подход. Другой способ — использовать THREE.Clock() и его метод .getDelta(). Взгляните на фрагмент кода.

const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 )
const renderer = new THREE.WebGLRenderer()

renderer.setSize( window.innerWidth, window.innerHeight )
camera.position.z = 0.5

document.body.appendChild( renderer.domElement )


const geo = new THREE.PlaneGeometry( 1, 1 )
const mat = new THREE.ShaderMaterial({
  vertexShader: `
    void main() {
      gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
    }
  `,
  fragmentShader: `
    uniform bool check;
    uniform float time;
    
    const float TAU = 6.2831;
    
    void main() {
      float fColor;
      
      fColor = ( sin( ( time * TAU ) ) + 1.0 ) / 2.0;
        
      gl_FragColor = vec4( 1.0, fColor, 1.0, 1.0 );
    }
  `,
  uniforms: {
    "check": { value: false },
    "time": { value: 1.0 },
  },
})

const plane = new THREE.Mesh( geo, mat )
scene.add( plane )

var clock = new THREE.Clock();
var time = 0;

const animate = function() {
  requestAnimationFrame( animate )
  
  time += clock.getDelta();

  plane.material.uniforms.time.value = time * 0.4; // make it 2.5 times slower

  renderer.render( scene, camera )
}

animate()
body {
  margin: 0;
}

canvas {
  width: 100%;
  height: 100%;
}

#config {
  position: absolute;
  color: #fff;
  cursor: pointer;
  user-select: none;
  font: bold 1em/1.5 sans-serif;
  padding: 1em;
}

#config label,
#config input {
  cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/89/three.js"></script>

person prisoner849    schedule 23.12.2017