Hello Guys.
i am trying to port the following shadertoy shader to Threejs: https://www.shadertoy.com/view/4sG3WV but i am failing miserably…
the problem with it is that im unsure the sequence that shadertoy renders the buffers (im assuming A, B, Image) and also rendering each Buffer separately i get completely different results from i believe it has something to do with the vec2 uv = fragCoord.xy / iResolution.xy;
as im translating it to: vec2 uv = vUv
this is a sample of the code:
import { Camera, LinearFilter, Mesh, OrthographicCamera, PlaneBufferGeometry, RGBAFormat, Scene, TextureLoader, Vector3, Vector4, WebGLRenderer, WebGLRenderTarget } from 'three'
import { BufferAShader } from './shaders/BufferAShader'
import { BufferBShader } from './shaders/BufferBShader'
import { BufferImageShader } from './shaders/BufferImageShader'
class App {
private width = window.innerWidth
private height = window.innerHeight
private renderer = new WebGLRenderer()
private loader = new TextureLoader()
private mousePosition = new Vector4()
private orthoCamera = new OrthographicCamera(0, this.width, 0, -this.height, 0, 10000)
private sceneA: Scene
private sceneB: Scene
private sceneC: Scene
private counter = 0
constructor() {
this.renderer.setSize(this.width, this.height)
document.body.appendChild(this.renderer.domElement)
this.renderer.domElement.addEventListener('mousedown', () => {
this.mousePosition.setZ(1)
this.counter = 0
})
this.renderer.domElement.addEventListener('mouseup', () => {
this.mousePosition.setZ(0)
})
this.renderer.domElement.addEventListener('mousemove', event => {
const x = (event.clientX / this.width) * 2 - 1
const y = -(event.clientY / this.height) * 2 + 1
this.mousePosition.setX(x)
this.mousePosition.setY(y)
})
}
private targetA = new BufferManager(this.renderer, { width: this.width, height: this.height })
private targetB = new BufferManager(this.renderer, { width: this.width, height: this.height })
private targetC = new BufferManager(this.renderer, { width: this.width, height: this.height })
private bufferA: BufferAShader
private bufferB: BufferBShader
private bufferImage: BufferImageShader
public start() {
const resolution = new Vector3(this.width, this.height, window.devicePixelRatio)
const channel0 = this.loader.load(require('./images/wallpaper.jpg'))
/**
* Target 1
*/
this.bufferA = new BufferAShader({
iFrame: { value: 0 },
iResolution: { value: resolution },
iMouse: { value: this.mousePosition },
iChannel0: { value: this.targetA.readBuffer.texture },
iChannel1: { value: this.targetB.readBuffer.texture }
})
this.sceneA = new Scene()
const meshA = new Mesh(new PlaneBufferGeometry(this.width, this.height), this.bufferA.material)
this.sceneA.add(meshA)
/**
* Target 2
*/
this.bufferB = new BufferBShader({
iFrame: { value: 0 },
iResolution: { value: resolution },
iMouse: { value: this.mousePosition },
iChannel0: { value: this.targetB.readBuffer.texture }
})
this.sceneB = new Scene()
const meshB = new Mesh(new PlaneBufferGeometry(this.width, this.height), this.bufferB.material)
this.sceneB.add(meshB)
this.bufferImage = new BufferImageShader({
iResolution: { value: resolution },
iMouse: { value: this.mousePosition },
iChannel0: { value: channel0 },
iChannel1: { value: null }
})
this.sceneC = new Scene()
const meshC = new Mesh(new PlaneBufferGeometry(this.width, this.height), this.bufferImage.material)
this.sceneC.add(meshC)
meshA.frustumCulled = false
meshB.frustumCulled = false
meshC.frustumCulled = false
meshA.position.set(this.width / 2, -this.height / 2, 0)
meshB.position.set(this.width / 2, -this.height / 2, 0)
meshC.position.set(this.width / 2, -this.height / 2, 0)
this.animate()
}
private animate() {
requestAnimationFrame(() => {
this.bufferA.uniforms[ 'iFrame' ].value = this.counter++
this.bufferA.uniforms[ 'iChannel0' ].value = this.targetA.readBuffer.texture
this.bufferA.uniforms[ 'iChannel1' ].value = this.targetB.readBuffer.texture
this.targetA.render(this.sceneA, this.orthoCamera)
this.bufferB.uniforms[ 'iChannel0' ].value = this.targetA.readBuffer.texture
this.targetB.render(this.sceneB, this.orthoCamera)
this.bufferImage.uniforms[ 'iChannel1' ].value = this.targetA.readBuffer.texture
this.targetC.render(this.sceneC, this.orthoCamera, true)
this.animate()
})
}
}
class BufferManager {
public readBuffer: WebGLRenderTarget
public writeBuffer: WebGLRenderTarget
constructor(private renderer: WebGLRenderer, { width, height }) {
this.readBuffer = new WebGLRenderTarget(width, height, {
minFilter: LinearFilter,
magFilter: LinearFilter,
format: RGBAFormat,
stencilBuffer: false
})
this.writeBuffer = this.readBuffer.clone()
}
public swap() {
const temp = this.readBuffer
this.readBuffer = this.writeBuffer
this.writeBuffer = temp
}
public render(scene: Scene, camera: Camera, toScreen: boolean = false) {
if (toScreen) {
this.renderer.render(scene, camera)
} else {
this.renderer.render(scene, camera, this.writeBuffer, true)
}
this.swap()
}
}
(new App()).start()
the fragment shader is pretty much a control c + control +v from shadertoy and replacing the relevant parts like gl_FragColor, gl_FragCoord
… main()
etc… and im using this for the vertexshader
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
I have uploaded the whole source code here: https://we.tl/JecAgsGlPA
(as i am a new member i am not able to upload attachment so i hosted it to on weTransfer)