如何处理 WebGL GLSL 着色器中的大数字?
How to handle big numbers in WebGL GLSL shaders?
如何在 GLSL 中处理大数字,例如下面的数字?
我正在提供一个着色器 Date.now()
作为统一,描述为:
The Date.now()
method returns the number of milliseconds elapsed since January 1, 1970 00:00:00 UTC. — MDN
例如,1514678400000
是一年的最后一天。
将此值传递给我的 ShaderMaterial
不会有太多结果,除非我将值缩小很多。
具体来说,这是行为似乎与我的预期不同的部分,它将最后 2500 毫秒映射到 0–1 范围内的值:
JavaScript: ( Date.now() % 2500 ) / 2500
GLSL: mod( time, 2500.0 ) / 2500.0
我更愿意在 GPU 上进行这些计算,但不确定我应该如何处理?
下面是一个说明问题的小场景:
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>
我正在为着色器提供 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>
如何在 GLSL 中处理大数字,例如下面的数字?
我正在提供一个着色器 Date.now()
作为统一,描述为:
The
Date.now()
method returns the number of milliseconds elapsed since January 1, 1970 00:00:00 UTC. — MDN
例如,1514678400000
是一年的最后一天。
将此值传递给我的 ShaderMaterial
不会有太多结果,除非我将值缩小很多。
具体来说,这是行为似乎与我的预期不同的部分,它将最后 2500 毫秒映射到 0–1 范围内的值:
JavaScript: ( Date.now() % 2500 ) / 2500
GLSL: mod( time, 2500.0 ) / 2500.0
我更愿意在 GPU 上进行这些计算,但不确定我应该如何处理?
下面是一个说明问题的小场景:
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>
我正在为着色器提供 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>