我正在使用几个反馈着色器循环(纹理乒乓)在three.js的网站上工作。
当有人访问该网站时,循环应该从某个点继续(取决于他/她访问的时间)。为了达到这个目的,我打算在第一帧中从服务器加载一张图片(例如一个jpeg),将其渲染到我的乒乓缓冲区并从第2帧开始继续我的正常反馈循环。
这是我的问题的精简版,作为反馈功能,我只需在前一帧中为像素的颜色添加一个小值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>feedbacktest</title>
<style>canvas { width: 100%; height: 100%; }</style>
</head>
<body>
<!-- Main THREE includes -->
<script src="js/three.min.js"></script>
<script src="js/Detector.js"></script>
<!-------------------->
<!-- Shaders -->
<!-------------------->
<!-- no change vertex shader. used for all render stages. -->
<script id="vs_output" type="x-shader/x-vertex">
varying vec2 texCoord;
void main(void)
{
texCoord = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<!-- feedback shader -->
<script id="fs_feedback" type="x-shader/x-fragment">
// switch on high precision floats
#ifdef GL_ES
precision highp float;
#endif
uniform sampler2D texture;
uniform sampler2D texture2;
varying vec2 texCoord;
uniform float onOpen;
void main()
{
// sample textures
vec4 result = texture2D(texture, texCoord);
vec4 startT = texture2D(texture2, texCoord);
result.rgb+=0.001;
result.rgb = mod(result.rgb, 1.0);
/* if (onOpen <=1.0){
result.rgb=startT.rgb;
}*/
result.a = 1.0;
gl_FragColor = result;
}
</script>
<!-- Final pass fragment shader. -->
<script id="fs_output" type="x-shader/x-fragment">
uniform sampler2D fb2output;
varying vec2 texCoord;
void main (void)
{
vec4 col = texture2D(fb2output, texCoord);
gl_FragColor = col;
}
</script>
<!-- init shader. -->
<script id="fs_start" type="x-shader/x-fragment">
uniform sampler2D texture;
varying vec2 texCoord;
void main (void)
{
vec4 col = texture2D(texture, texCoord);
gl_FragColor = col;
}
</script>
<!-------------------->
<!-- Main Logic -->
<!-------------------->
<script>
if (!Detector.webgl)
{
Detector.addGetWebGLMessage();
}
//------------------------------------------
// Globals
//------------------------------------------
var cameraLoop, cameraOutput, sceneFeedback, sceneOutput, renderer, sceneStart;
var feedbackTexture, feedbackTexture2, loadTexture;
var feedbackUniforms, mainUniforms, startUniforms;
var feedbackQuad, screenQuad, startQuad;
var feedbackMat, screenMat, startMat;
var loopRes = new THREE.Vector2(64.0, 64.0);
var outputRes = new THREE.Vector2(512.0, 512.0);
var doLoad =0.0;
// var onOpen = 0.0;
var renderTargetNearestFloatParams = {
minFilter:THREE.NearestFilter,
magFilter:THREE.NearestFilter,
wrapS:THREE.ClampToEdgeWrapping,
wrapT:THREE.ClampToEdgeWrapping,
format:THREE.RGBAFormat,
stencilBuffer:false,
depthBuffer:false,
needsUpdate:true,
type:THREE.FloatType
};
//------------------------------------------
// Main init and loop
//------------------------------------------
start();
update();
//------------------------------------------
// Initialization
//------------------------------------------
function start()
{
//setup scenes
sceneOutput = new THREE.Scene();
sceneFeedback = new THREE.Scene();
sceneStart = new THREE.Scene();
//setup renderer
renderer = new THREE.WebGLRenderer({ precision:"highp"});
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor(0x808080);
renderer.autoClear = false;
document.body.appendChild( renderer.domElement );
// create buffers
feedbackTexture = new THREE.WebGLRenderTarget( loopRes.x, loopRes.y, renderTargetNearestFloatParams );
feedbackTexture2 = new THREE.WebGLRenderTarget( loopRes.x, loopRes.y, renderTargetNearestFloatParams );
// load a texture, set wrap mode
var loadTexture = THREE.ImageUtils.loadTexture( "textures/tes2t.jpg" );
loadTexture.wrapS = THREE.ClampToEdgeWrapping;
loadTexture.wrapT = THREE.ClampToEdgeWrapping;
loadTexture.minFilter = THREE.NearestFilter;
loadTexture.magFilter = THREE.NearestFilter;
loadTexture.format = THREE.RGBAFormat;
loadTexture.type = THREE.FloatType;
// Setup algorithm camera
cameraLoop = new THREE.OrthographicCamera( loopRes.x / - 2, loopRes.x / 2, loopRes.y / 2, loopRes.y / - 2, -10000, 10000 );
// Setup sceneOutput camera
cameraOutput = new THREE.PerspectiveCamera( 60, window.innerWidth/window.innerHeight, 1, 10000 );
cameraOutput.position.z = 300;
// feedback shader
feedbackUniforms = {
texture: { type: "t", value: feedbackTexture2 },
texture2: { type: "t", value: loadTexture },
onOpen: { type: "f", value: 0.0 },
};
feedbackMat = new THREE.ShaderMaterial({
uniforms: feedbackUniforms,
vertexShader: document.getElementById( 'vs_output' ).textContent,
fragmentShader: document.getElementById( 'fs_feedback' ).textContent
});
var feedbackGeo = new THREE.PlaneBufferGeometry( loopRes.x, loopRes.y );
feedbackQuad = new THREE.Mesh( feedbackGeo, feedbackMat );
feedbackQuad.position.z = -100;
sceneFeedback.add( feedbackQuad );
// output shader
mainUniforms = {
fb2output: { type: "t", value: feedbackTexture2 },
};
screenMat = new THREE.ShaderMaterial({
uniforms: mainUniforms,
vertexShader: document.getElementById( 'vs_output' ).textContent,
fragmentShader: document.getElementById( 'fs_output' ).textContent,
});
var screenGeo = new THREE.PlaneBufferGeometry( outputRes.x, outputRes.y );
sceneQuad = new THREE.Mesh( screenGeo , screenMat );
sceneQuad.position.z = -200;
sceneOutput.add( sceneQuad );
// init shader
startUniforms = {
texture: { type: "t", value: loadTexture },
};
startMat = new THREE.ShaderMaterial({
uniforms: startUniforms,
vertexShader: document.getElementById( 'vs_output' ).textContent,
fragmentShader: document.getElementById( 'fs_start' ).textContent,
});
var startGeo = new THREE.PlaneBufferGeometry( loopRes.x, loopRes.y );
startQuad = new THREE.Mesh( startGeo , startMat );
startQuad.position.z = -100;
sceneStart.add( startQuad );
}
//------------------------------------------
// Main loop
//------------------------------------------
function update()
{
requestAnimationFrame( update );
console.debug(doLoad.toString());
render();
}
//------------------------------------------
// Main rendering
//------------------------------------------
function render()
{
renderer.clear();
if (doLoad < 1.0){
renderer.render( sceneStart, cameraLoop, feedbackTexture2);
doLoad = 1.0;
} else {
renderer.render( sceneFeedback, cameraLoop, feedbackTexture);
var a = feedbackTexture2;
feedbackTexture2 = feedbackTexture;
feedbackTexture = a;
feedbackUniforms.texture.value = feedbackTexture2;
}
renderer.render( sceneOutput, cameraOutput );
// feedbackUniforms.onOpen.value += 0.5;
}
</script>
</body>
</html>
正如你在渲染函数中看到的那样,我试图在第1帧中渲染sceneStart,然后在场景中渲染sceneFeedback(如果/ else阻止)。不幸的是,这不起作用。我已经尝试了各种各样的东西,也在着色器本身切换到开始纹理(参见注释代码),但没有运气。 我发现当我改变这条线时
doLoad = 1.0;
到
doLoad +=0.4;
或低于0.5的任何东西都可以。据我所知,它必须在我的反馈缓冲区中写入3次,直到正常循环可以在那里工作....但为什么?
在第一帧中写入反馈纹理也不起作用,正如您可能建议的那样......
不幸的是,将它渲染为3帧并不是我的解决方案,因为它会破坏我所涉及的实际着色器之一并且还会带来其他问题,这些问题比在第一帧中加载图片要复杂得多。 ...
在旁注中,我如何调试three.js应用程序的第一帧?我知道WebGL Inspector但是如果我在那里减慢帧速率然后刷新播放设置又恢复正常......有什么建议吗?
非常感谢你!答案 0 :(得分:1)
我很确定在开始渲染之前需要等待图像加载。图像加载异步,因此您的前几帧不会加载纹理。
看起来你正在使用THREE.ImageUtils.loadTexture
。 According to the docs loadTexture
需要4个参数(.loadTexture (url, mapping, onLoad, onError)
)第3个是加载图像时的回调。
在加载图像之前,您可能不想渲染。在您的代码的开头,您有
start();
update(); // delete this line
删除更新行,然后将loadTexture
行更改为
// load a texture, set wrap mode
var loadTexture = THREE.ImageUtils.loadTexture(
"textures/tes2t.jpg", undefined, update );
当图像加载并开始渲染时,将调用update
。