steveblue
steveblue

Reputation: 490

GLSL Shader vertex displacement only on 1 vertex

I'm trying to refactor a decade old GLSL vertex displacement shader that used to work with a legacy version of Three.js and I ran into a bunch of issues from the order of operations for textures loading from the video source to refactoring the shader to be more accurate for the compiler. I think I have fixed most of the issues save one (I hope). Please help.

Live code example is on StackBlitz. https://stackblitz.com/edit/vitejs-vite-t6qqxpfh?file=src%2Fmain.ts

The vertex displacement is driven from the brightness value of pixels in a video texture and the dimensions used to be mapped to the dimensions of the geometry, but now that does not seem to be the case and the GLSL shader is displacing only one vertex.

GLSL Shader only displacing one pixel

It used to do something like this:

The legacy shader working with legacy Three.js

The legacy code is messy at best, so I will only paste the legacy GLSL shader that worked here:

RuttEtraShader = {

        uniforms: {

            "tDiffuse": { type: "t", value: null },
            "multiplier":  { type: "f", value: 13.3 },
            "displace":  { type: "f", value: 7.3 },
            "opacity":  { type: "f", value: 1.0 },
            "originX":  { type: "f", value: 0.0 },
            "originY":  { type: "f", value: 0.0 },
            "originZ":  { type: "f", value: 0.0 }

        },
        vertexShader: [
        'precision highp int;',
        'precision highp float;',
        'uniform sampler2D tDiffuse;',
        'varying vec3 vColor;',
        'varying vec2 vUv;',
        'uniform float displace;',
        'uniform float multiplier;',
        'uniform float originX;',
        'uniform float originY;',
        'uniform float originZ;',

        'void main() {',
            'vec4 newVertexPos;',
            'vec4 dv;',
            'float df;',
            'vUv = uv;',
            'vec3 origin = vec3 (originX,originY,originZ);',
            'dv = texture2D( tDiffuse, vUv.xy );',
            'df = multiplier*dv.x + multiplier*dv.y + multiplier*dv.z;',
            'newVertexPos = vec4( normalize(position - origin) * df * vec3 (1.0, 1.0, displace), 0.0 ) + vec4( position, 1.0 );',
            'vColor = vec3( dv.x, dv.y, dv.z );',

            'gl_Position = projectionMatrix * modelViewMatrix * newVertexPos;',
        '}'

        ].join("\n"),

        fragmentShader: [

        'varying vec3 vColor;',
        'uniform float opacity;',

          'void main() {',

              'gl_FragColor = vec4( vColor.rgb, opacity );',
          '}'

        ].join("\n")

};

The new GLSL shader I'm trying to get working with three 0.171.0 appears like so:

const RuttEtraShader = {
        name: 'RuttEtraShader',
        uniforms: {
            'tDiffuse': { value: null },
            'multiplier':  { value: 13.3 },
            'displace':  { value: 3.3 },
            'opacity':  { value: 1.0 },
            'originX':  { value: 0.0 },
            'originY':  { value: 0.0 },
            'originZ':  { value: 0.0 }
        },
        vertexShader: `
            precision highp int;
            precision highp float;
            attribute vec2 uv;
            uniform sampler2D tDiffuse;
            varying vec2 vUv;
            varying vec3 vColor;
            varying vec3 position;
            uniform float displace;
            uniform float multiplier;
            uniform float originX;
            uniform float originY;
            uniform float originZ;
            uniform mat4 modelViewMatrix;
            uniform mat4 projectionMatrix;

            void main() {
                vec4 newVertexPos;
                vec4 dv;
                float df;
                vec3 origin = vec3(originX, originY, originZ);
                vUv = uv;
                dv = texture2D( tDiffuse, vUv.xy );
                df = multiplier * dv.x + multiplier * dv.y + multiplier * dv.z;
                newVertexPos = vec4( normalize(position - origin) * df * vec3(1.0, 1.0, displace), 0.0 ) + vec4( position, 1.0 );
                vColor = vec3( dv.x, dv.y, dv.z );
                gl_Position = projectionMatrix * modelViewMatrix * newVertexPos;
            }`,

        fragmentShader: `
            precision highp float;
            varying vec3 vColor;
            uniform float opacity;

            void main() {

                gl_FragColor = vec4( vColor.rgb, opacity );

            }`

};

Three.js code:

import { OrthographicCamera, Scene, Color, PlaneGeometry, Float32BufferAttribute, Uint8BufferAttribute, RawShaderMaterial, DoubleSide, Mesh, WebGLRenderer, VideoTexture, LinearFilter, RGBAFormat, WebGLRenderTarget, NearestFilter, AmbientLight, SpotLight } from 'three';
import { RuttEtraShader } from './shaders/RuttEtraShader';
import { EffectComposer } from './postprocessing/EffectComposer';
import { RenderPass } from './postprocessing/RenderPass';
import { BloomPass } from './postprocessing/BloomPass';
import { HueSaturationShader } from './shaders/HueSaturationShader';
import { ShaderPass } from './postprocessing/ShaderPass';


export class Synth {
    container: HTMLElement;
    camera: OrthographicCamera;
    scene: Scene;
    geometry: PlaneGeometry;
    material: RawShaderMaterial;
    mesh: Mesh;
    renderer:  WebGLRenderer;
    videoInput: HTMLVideoElement;
    texture: any;
    fill: AmbientLight;
    key: SpotLight;
    back: SpotLight;
    composer: EffectComposer;
    renderModel: RenderPass;
    effectBloom: BloomPass;
    effectHue: ShaderPass;
    constructor(container: HTMLElement, videoInput: HTMLVideoElement) { 
        this.container = container;
        this.videoInput = videoInput;
        
        this.camera = new OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, 1, 10000);
        this.camera.position.x = 0;
        this.camera.position.y = -1130;
        this.camera.position.z = 1680;

        this.scene = new Scene();
        this.scene.background = new Color( 0x000000 );

        this.geometry = new PlaneGeometry(640, 480);

        this.mesh = new Mesh( this.geometry );
        this.scene.add( this.mesh ); 

        this.fill = new AmbientLight(0x707070); // soft white light
        this.scene.add(this.fill);
    
        this.key = new SpotLight(0xffffff);
        this.key.position.set(0, 0, 5000).normalize();
        this.key.target = this.mesh;
    
        this.key.intensity = 10;
        this.key.castShadow = true;
        this.scene.add(this.key);
    
        this.back = new SpotLight(0xffffff);
        this.back.position.set(0, 0, -5000).normalize();
        this.back.target = this.mesh;
    
        this.back.intensity = 100;
        this.back.castShadow = true;
        this.scene.add(this.back);

        const renderTarget = new WebGLRenderTarget(
            window.innerWidth, window.innerHeight, {minFilter: LinearFilter, magFilter: NearestFilter});
        
        this.renderer = new WebGLRenderer({
            antialias: true,
        });
        this.renderer.setRenderTarget(renderTarget);
        this.renderer.setPixelRatio( window.devicePixelRatio );
        this.renderer.setSize( window.innerWidth, window.innerHeight );
        this.renderer.setAnimationLoop( this.animate.bind(this) );
    

        this.composer = new EffectComposer(this.renderer, this.renderer.getRenderTarget());
        this.composer.setSize(window.innerWidth, window.innerHeight);
        this.renderer.getRenderTarget().setSize( window.innerWidth, window.innerHeight );
  
        this.renderModel = new RenderPass(this.scene, this.camera);
        this.composer.addPass(this.renderModel);
    
        this.effectBloom = new BloomPass(3.3, 20, 4.0);
        this.effectBloom = new BloomPass(1.5, 5.0, 1.4);
        this.composer.addPass(this.effectBloom);
    
        this.effectHue = new ShaderPass(HueSaturationShader, null);
        this.composer.addPass(this.effectHue);
        
        this.videoInput.addEventListener("loadeddata", this.load.bind(this));
        this.videoInput.load();
        this.videoInput.loop = true;

        window.addEventListener('resize', this.onWindowResize.bind(this), false);

        this.container.appendChild( this.renderer.domElement );
   

    }

    load() {
        this.scene.remove(this.mesh);
        this.texture = new VideoTexture(this.videoInput);
        this.texture.minFilter = LinearFilter;
        this.texture.magFilter = LinearFilter;
        this.texture.format = RGBAFormat;
        this.texture.generateMipmaps = false;
        this.material = new RawShaderMaterial({
            uniforms: {
                tDiffuse: { value: this.texture },
                originX: { value: 0.0 },
                originY: { value: 0.0 },
                originZ: { value: -2000.0 },
                opacity: { value: 0.95 },
                multiplier:  { value: 13.3 },
                displace:  { value: 3.3 },
            },
            vertexShader: RuttEtraShader.vertexShader,
            fragmentShader: RuttEtraShader.fragmentShader,
            depthWrite: true,
            depthTest: true,
            wireframe: true,
            transparent: true,
            side: DoubleSide
        });
        this.mesh = new Mesh( this.geometry, this.material );
        this.scene.add( this.mesh );
    }

    animate() {
        const time = performance.now();
        this.camera.aspect = window.innerWidth / window.innerHeight;
        this.texture && (this.texture.needsUpdate = true);
        this.material && (this.material.needsUpdate = true);
        this.material && (this.material.renderToScreen = true);
        this.material && (this.material.wireframe = true);
        this.camera.lookAt(this.scene.position);
        this.renderer.render( this.scene, this.camera );
    }

    onWindowResize() {
        this.camera.aspect = window.innerWidth / window.innerHeight;
        this.camera.updateProjectionMatrix();
        this.renderer.setSize(window.innerWidth, window.innerHeight);
        this.composer.setSize(window.innerWidth, window.innerHeight);
        this.renderer.getRenderTarget().setSize( window.innerWidth, window.innerHeight );
    }
}

I'm guessing I'm missing some configuration that is now required that wasn't before or there seems to be a compiler now for GLSL that is much more robust that perhaps introduced a breaking change and the syntax for the shader is wrong, although I'm not seeing any errors. I reduced the logic to a simple example with the latest THREE.

Upvotes: 0

Views: 50

Answers (1)

steveblue
steveblue

Reputation: 490

I was using RawShaderMaterial instead of ShaderMaterial. That was the problem. I removed the duplicate variables declared in the shader and it worked.

Upvotes: 1

Related Questions