ThreeJS PingPong纹理渲染无报错但无画面问题求助
新版ThreeJS中PingPong纹理交换无渲染内容问题排查
问题背景
尝试用ThreeJS的PingPong纹理交换实现烟雾效果,参考旧版教程适配最新版后,代码无报错但屏幕完全无渲染内容。已完成教程第一步(纹理交换+黑渐红着色器),旧版代码可正常运行,已适配新版.render()仅接受两个参数的变化,但仍无效。
原代码
JavaScript代码
import * as THREE from 'three'; import FragmentShader from './main.frag'; var scene; var camera; var renderer; function scene_setup(){ scene = new THREE.Scene(); var width = window.innerWidth; var height = window.innerHeight; camera = new THREE.OrthographicCamera( width / - 2, width / 2, height / 2, height / - 2, 1, 1000 ); camera.position.z = 2; renderer = new THREE.WebGLRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); document.body.appendChild( renderer.domElement ); } scene_setup(); var bufferScene; var textureA; var textureB; var bufferMaterial; var plane; var bufferObject; var finalMaterial; var quad; function buffer_texture_setup(){ bufferScene = new THREE.Scene(); textureA = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter }); textureB = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter } ); bufferMaterial = new THREE.ShaderMaterial( { uniforms: { bufferTexture: { type: "t", value: textureA }, res : {type: 'v2',value:new THREE.Vector2(window.innerWidth,window.innerHeight)} }, fragmentShader: FragmentShader } ); plane = new THREE.PlaneGeometry( window.innerWidth, window.innerHeight ); bufferObject = new THREE.Mesh( plane, bufferMaterial ); bufferScene.add(bufferObject); finalMaterial = new THREE.MeshBasicMaterial({map: textureB}); quad = new THREE.Mesh( plane, finalMaterial ); scene.add(quad); } buffer_texture_setup(); function render() { requestAnimationFrame( render ); renderer.setRenderTarget(textureB); renderer.clear(); renderer.render(bufferScene,camera); var t = textureA; textureA = textureB; textureB = t; quad.material.map = textureB; bufferMaterial.uniforms.bufferTexture.value = textureA; renderer.render( scene, camera ); } render();
片段着色器代码
uniform vec2 res; uniform sampler2D bufferTexture; void main() { vec2 pixel = gl_FragCoord.xy / res.xy; gl_FragColor = texture2D( bufferTexture, pixel ); gl_FragColor.r += 0.01; }
问题原因及修复方案
1. 初始纹理未初始化
textureA初始为空白纹理,第一次渲染时着色器读取无内容的纹理,后续叠加无法显示。需先渲染黑色初始帧到textureA。
2. 平面尺寸与正交相机不匹配
使用THREE.PlaneGeometry(window.innerWidth, window.innerHeight)会导致平面超出正交相机视锥体范围,应改用单位平面THREE.PlaneGeometry(2, 2),刚好铺满正交相机视口。
3. 渲染目标类型与着色器兼容性
新版ThreeJS中,WebGLRenderTarget默认类型可能截断颜色值,需明确设置type: THREE.FloatType以支持浮点颜色值叠加。
4. 纹理更新未触发材质更新
交换纹理后需设置material.needsUpdate = true,否则ThreeJS不会重新加载纹理。
5. 着色器变量兼容性
新版ThreeJS推荐使用out vec4 fragColor替代旧的gl_FragColor,提升兼容性。
修改后的代码
JavaScript代码
import * as THREE from 'three'; import FragmentShader from './main.frag'; let scene, camera, renderer; function scene_setup() { scene = new THREE.Scene(); const width = window.innerWidth; const height = window.innerHeight; camera = new THREE.OrthographicCamera(width / -2, width / 2, height / 2, height / -2, 1, 1000); camera.position.z = 2; renderer = new THREE.WebGLRenderer(); renderer.setSize(width, height); document.body.appendChild(renderer.domElement); } scene_setup(); let bufferScene, textureA, textureB, bufferMaterial, bufferObject, finalMaterial, quad; function buffer_texture_setup() { bufferScene = new THREE.Scene(); // 明确设置渲染目标类型为FloatType,支持浮点颜色值 const renderTargetOpts = { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, type: THREE.FloatType }; textureA = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, renderTargetOpts); textureB = new THREE.WebGLRenderTarget(window.innerWidth, window.innerHeight, renderTargetOpts); bufferMaterial = new THREE.ShaderMaterial({ uniforms: { bufferTexture: { value: textureA }, res: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) } }, fragmentShader: FragmentShader }); // 使用单位平面,适配正交相机视口 const plane = new THREE.PlaneGeometry(2, 2); bufferObject = new THREE.Mesh(plane, bufferMaterial); bufferScene.add(bufferObject); finalMaterial = new THREE.MeshBasicMaterial({ map: textureB }); quad = new THREE.Mesh(plane, finalMaterial); scene.add(quad); // 初始化textureA为黑色 renderer.setRenderTarget(textureA); renderer.clearColor(0, 0, 0, 1); renderer.clear(); renderer.setRenderTarget(null); } buffer_texture_setup(); function render() { requestAnimationFrame(render); // 渲染到textureB renderer.setRenderTarget(textureB); renderer.clear(); renderer.render(bufferScene, camera); // 交换纹理并更新材质 [textureA, textureB] = [textureB, textureA]; quad.material.map = textureB; quad.material.needsUpdate = true; bufferMaterial.uniforms.bufferTexture.value = textureA; bufferMaterial.needsUpdate = true; // 渲染到屏幕 renderer.setRenderTarget(null); renderer.render(scene, camera); } render();
片段着色器代码
uniform vec2 res; uniform sampler2D bufferTexture; out vec4 fragColor; // 替代gl_FragColor void main() { vec2 pixel = gl_FragCoord.xy / res.xy; fragColor = texture(bufferTexture, pixel); // 使用texture替代texture2D fragColor.r += 0.01; // 确保颜色值不超过1.0,防止溢出 fragColor.r = min(fragColor.r, 1.0); }
内容的提问来源于stack exchange,提问作者eliza
相关产品推荐
相关产品推荐

