问题描述
我正在使用多个反馈着色器循环(纹理乒乓)在Three.js中的网站上工作。
I'm working on a website in three.js using several feedback shader loops (texture pingpong).
当某人访问该站点时,循环应该从某个点继续(取决于他/她访问的时间)。为此,我打算在第一帧中从服务器加载图片(例如jpeg),将其渲染到我的乒乓缓冲区中,并从第2帧开始继续我的正常反馈循环。
When someone visits the site, the loop should continue from a certain point (depending on when he/she visits). To achieve this I intend to load a picture (eg a jpeg) from the server in the first frame, render this to my pingpong buffers and continue with my normal feedback loop from frame 2 onwards.
这是我的问题的简化版本,作为反馈功能,我只是在前一帧的像素颜色中添加了一个小值。
Here's a stripped down version of my problem, as a feedback function i simply add a small value to the color of the pixel in the previous frame.
<!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>
正如您在render函数中所看到的,我试图渲染sceneStart在第1帧和sceneFeedback (如果/其他块)。不幸的是,这行不通。我尝试了各种方法,也进行了着色器本身到起始纹理的切换(请参见注释的代码),但是没有运气。
我发现当我更改行时
As you can see in the render function I'm trying to render sceneStart in Frame 1 and sceneFeedback afterwards (if/else block). Unfortunately, this doesn't work. I've tried all kinds of stuff, also doing the switch to the start texture in the shader itself (see commented code), but no luck.I found out that when I change the line
doLoad = 1.0;
到
doLoad +=0.4;
或低于0.5的任何值都会起作用。因此,据我所知,它必须在我的反馈缓冲区中写入3次,直到正常的循环可以从那里开始....但是为什么?
or anything below 0.5 it will work. So as I understand it has to write 3 times into my feedback buffer until the normal loop can work from there....but WHY?
在正如您可能会建议的那样,第一帧也不起作用。...
Writing into both feedback textures in the first frame also doesn't work, as you might suggest....
不幸的是,将其渲染为3帧对我来说不是解决方案,因为它将破坏一个我实际着色器的使用,还提出了其他问题,这些问题据说比在第一帧中加载图片要复杂得多.....
Unfortunately, rendering it for 3 frames is no solution for me, as it will break one of my actual shaders involved and also brings up other issues which are supposedly way more complicated to solve than loading a picture in the first frame.....
,我该如何调试Three.js应用程序的第一帧?我知道WebGL Inspector,但是如果我放慢那里的帧速率,然后刷新播放设置又恢复正常了,有什么建议吗?
On a sidenote, how can i debug the very first frames of a three.js app? I'm aware of WebGL Inspector but if I slow down the framerate there and then refresh playback settings are back to normal...any suggestions?
非常感谢!
推荐答案
我很确定您需要等待图像加载后才能开始渲染。图像会异步加载,因此您的前几帧将尚未加载纹理。
I'm pretty sure you need to wait for the image to load before you start rendering. Images load async so your first few frames will not have the texture loaded yet.
您似乎正在使用 THREE.ImageUtils.loadTexture
。 loadTexture
接受4个参数( .loadTexture(URL,映射,onLoad,onError)
),第三个参数是加载图像时的回调。
It looks like you're using THREE.ImageUtils.loadTexture
. According to the docs loadTexture
takes 4 arguments (.loadTexture (url, mapping, onLoad, onError)
) the 3rd one is a callback when the image is loaded.
在加载图像之前,您可能不想渲染。在代码的开头,您已经
You probably don't want to render until the image is loaded. At the beginning of your code you have
start();
update(); // delete this line
删除更新行,然后更改 loadTexture
行到
Delete the update line, then change your loadTexture
line to
// load a texture, set wrap mode
var loadTexture = THREE.ImageUtils.loadTexture(
"textures/tes2t.jpg", undefined, update );
这样的话, update
将被调用图片加载完毕并开始渲染。
That way update
will be called when the image is finished loading and start rendering.
这篇关于在three.js中使用交换的缓冲区在反馈循环的第一帧中加载文件纹理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!