Utilize a WebGLRenderTarget in a recursive manner with the power of threejs and GLSL programming

As a newcomer to both ThreeJS and GLSL, I am exploring ways to reuse the output of a fragment shader in another shader, while also utilizing that same output in the subsequent frame of the same shader. Essentially, the output of the shader is dependent on the output of the previous frame. My approach involves using WebGLRenderTarget to achieve this goal.

Below is the code snippet I am working with:


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()

While attempting to update the texture using

scene.children[0].material.uniforms.iFire.value = fireLayer.texture
, I encountered an illegal feedback error. Additionally, trying {...fireLayer.texture} did not display anything, possibly due to issues with image copying.

How can I efficiently copy the output of a fragment shader and utilize it within the same shader that generated it?

Answer №1

If you attempt to both read and write to the same image, you will encounter unpredictable outcomes and an illegal feedback error. To avoid this issue, it is recommended to utilize a FramebufferTexture. Copy the shader's output into the framebuffer texture and then use this texture as input for the subsequent frame:

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)
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Customize the appearance of polygons in OpenLayers 2 by adjusting the fill color and opacity

I am aiming to use different fill colors and opacity values for various shapes (such as Triangle, Circle, Square, Hexagon, etc.). Although I can draw the shapes, set different titles, and stroke colors with the code below, I'm struggling to define th ...

Leverage async and await features in TypeScript aiming at ES5 compatibility

Currently working on a TypeScript project that is set to target ES5, I am exploring the feasibility of incorporating async/await functionality. Syntactically, the TypeScript compiler is able to transpile the code without issues. However, it has come to my ...

Tips for creating a new tab or window for a text document

Below code demonstrates how to open a new tab and display an image with a .jpg extension. Similarly, I want to be able to open a text document (converted from base64 string) in a new tab if the extension is .txt. success: function(data) { var ...

Utilizing a single variable across different JavaScript scripts - where one script includes a .ready function while the other does not

I am facing a challenge with 2 javascript scripts that I have: <script type="text/javascript"> function capture(){ var canvas = document.getElementById('canvas'); var video = document.getElementById('video'); canvas.getContext ...

Tutorial on creating a loop for a function based on a specific condition

I have created two different div classes named box and box2. The box2 class can be dragged and dropped into any of the three box classes. Each box contains randomly chosen values from an array, while box2 contains its corresponding image for display. When ...

The jQuery .load method is having trouble loading a local HTML file within a Cocoa WebView

Having an issue with my Cocoa WebView where I'm attempting to load a local HTML file using jQuery.load(). My code snippet looks like this: $("#mydiv").load("test.html"); // The test.html file is located within the app's bundle Although no exce ...

Ensure that the file exists before including it in the $routeProvider template for ng-include

Using Angular routes, I have set up a system where the slug from the URL determines which file to load. The code looks like this: $routeProvider.when("/project/:slug", { controller: "ProjectController", template: function($routeParams){ return & ...

Icons are failing to display in the dropdown menu of MUI after an option is selected in ReactJS

There seems to be an issue with the icon in the select tag. Initially, it is not showing which is correct. However, when you click the dropdown, the icon appears as expected. But after selecting an option, if you click the dropdown again, the icon disapp ...

The Power of Json, Ajax, and Javascript

Is there a way to regularly check for updates and update the relevant cell accordingly? I want the updated cell to flash and change color to red/green based on if it is a negative or positive numeric value. I will be using JQuery, Ajax, and JSON... Best ...

What could be causing my function to not successfully retrieve elements based on their text content?

A function called matchTagAndText is designed to take two arguments: a selector and text, checking if any matched elements contain the specified text. The function code is as follows: function matchTagAndText(sel, txt) { var elements = document.queryS ...

Maintaining duplicate values in a JSON stringify operation: Tips for retention

Server responses are being received in JSON format, which may contain objects with duplicate keys. When displaying the results, I noticed that only the last value for duplicate keys was showing up. After investigating, I realized this was due to using the ...

Trouble with ES6 Arrow Functions, Syntax Error

I am encountering an issue with my JS class structure: class Tree { constructor(rootNode) { this._rootNode = rootNode; rootNode.makeRoot(); } getRoot() { return this._rootNode; } findNodeWithID(id) ...

How can I attach a click event to the left border of a div using jQuery?

I am wondering about a div element <div id="preview"> </div> Can anyone suggest a method to attach a click event specifically to the left border of this div? Your help is greatly appreciated. ...

Determine your age by manually inputting your date of birth

Utilizing Ajax Calendar, I have successfully implemented a feature to calculate age based on the date selected in one textbox and display it in another. However, I am facing two challenges. First Issue:- I want the Age Textbox to be disabled so users cann ...

Using Geolocation in HTML5 and JavaScript

Currently, I am working on my debut mobile web application and have successfully integrated Google Maps into it using Geolocation JavaScript API version 3. Now, I am looking to add a button that, when clicked by the user, centers the map on my location o ...

What is the method for establishing session or cookie in an HTTPS request to a specific URL?

When making an HTTPS GET request in Node.js to a specific URL, it is necessary to include the session cookie. In this case, I already have the value of the cookie that needs to be sent. var URL = require('url-parse'); var appurl = "https://test ...

Proto-Type Namespace

I have a class named "Animals" which serves as a namespace for other classes, "Crocodile" and "Monkey": var Monkey = function(Animals) { this.Animals = Animals; }; Monkey.prototype.feedMe = function() { this.Animals.feed(); }; var Crocodile = functi ...

What is the process for implementing a third-party component in my web application?

After some experimentation, I've discovered that it's necessary to include the link to the css files in the header and then mention the link to the js files before the closing tag. However, I encountered difficulties when trying to use a compone ...

Live streaming updates directly from Firebase

In order to achieve real-time updates from Firebase, my objective is to retrieve comments from Firebase and display them on this.note. I seem to have made a mistake in the update() function. Here is the tutorial I followed: link. methods: { update(){ db.c ...

JavaScript fails to effectively validate URLs

I have implemented the following validation for URLs: jQuery.validator.addMethod("urlValidatorJS", function (value, element) { var regExp = (/^HTTP|HTTP|http(s)?:\/\/(www\.)?[A-Za-z0-9]+([\-\.]{1}[A-Za-z0-9]+)*\.[A-Za-z]{ ...