使用 Threejs 和 GLSL 递归地使用 WebGLRenderTarget

问题描述 投票:0回答:1

我对 ThreeJS 和 GLSL 都是新手。我试图在另一个片段着色器中重用一个片段着色器的输出,并在下一帧的同一着色器中使用相同的输出。换句话说,着色器的输出取决于前一帧中同一着色器的输出。我正在尝试使用 WebGLRenderTarget 来实现这一目标。

这是我的代码:

import * as THREE from "three"
var camera, camera_2, fireShader, mainShader, renderer, scene, scene_2
const width = 1200
const height = 720
const texture = new THREE.TextureLoader().load('./images/00_map.png')
var fire = new THREE.TextureLoader().load('./images/fire_01.png')

const fireLayer = new THREE.WebGLRenderTarget(width, height)
const uniforms = {
    iTime: { value: 0 },
    iResolution: { value: new THREE.Vector3(width, height, 1) },
    iTexture: { value: texture},
    iFire: { value: fire }
}
const now = new Date()

async function init() {
    const stage = document.getElementById('stage')
    renderer = new THREE.WebGLRenderer({antialias: true})
    scene = new THREE.Scene()
    camera = new THREE.OrthographicCamera(width / -2, width / 2, height / 2, height / -2, 0.1, 20)
    camera.position.z = 5

    renderer.setSize(width, height)
    stage.appendChild(renderer.domElement)

    mainShader = await (await fetch('shaders/frag.glsl')).text()
    
    const geometry = new THREE.PlaneGeometry(width, height)    
    const material = new THREE.ShaderMaterial({
        uniforms,
        fragmentShader: mainShader
    })
    material.needsUpdate = true

    const plane = new THREE.Mesh(geometry, material)

    scene.add(plane)    
}

async function init_2 () {
    scene_2 = new THREE.Scene()
    camera_2 = new THREE.OrthographicCamera(width / -2, width / 2, height / 2, height / -2, 0.1, 20)
    camera_2.position.z = 5

    fireShader = await (await fetch('shaders/fire.glsl')).text()
    
    const geometry = new THREE.PlaneGeometry(width, height)    
    const material = new THREE.ShaderMaterial({
        uniforms,
        fragmentShader: fireShader
    })
    material.needsUpdate = true
    const plane = new THREE.Mesh(geometry, material)

    scene_2.add(plane)   
}

function render() {
    uniforms.iTime.value = (new Date() - now) * .001
    renderer.setRenderTarget(fireLayer)
    renderer.render(scene_2, camera_2)
    if (scene.children[0])
        scene.children[0].material.uniforms.iFire.value = fireLayer.texture
    renderer.setRenderTarget(null)
    renderer.render(scene, camera)
}

function animate() {    
    render()
    requestAnimationFrame(animate)
}

init()
init_2()
animate()

更新纹理如上所示,即

scene.children[0].material.uniforms.iFire.value = fireLayer.texture
导致非法反馈错误。当我尝试使用
{...fireLayer.texture}
更新纹理时,没有任何内容显示。我怀疑这是因为这样复制并没有复制图像。

如何正确复制片段着色器的输出并在生成它的同一着色器中使用它?

javascript three.js glsl
1个回答
0
投票

您必须使用

FramebufferTexture
。将着色器的输出复制到帧缓冲区纹理中,并使用该纹理作为下一帧的输入:

const fireLayerFbCopy = new THREE.FramebufferTexture(width, height)
const fireLayer = new THREE.WebGLRenderTarget(width, height)
function render() {
    uniforms.iTime.value = (new Date() - now) * .001
    renderer.setRenderTarget(fireLayer)
    renderer.render(scene_2, camera_2)
    if (scene.children[0])
        scene.children[0].material.uniforms.iFire.value = fireLayerFbCopy
    renderer.copyFramebufferToTexture(new THREE.Vector2(), fireLayerFbCopy);
    renderer.setRenderTarget(null)
    renderer.render(scene, camera)
}
© www.soinside.com 2019 - 2024. All rights reserved.