1

I'm exploring shaders in Three.JS and trying to create a simple tv-static effect. From what I understand, I need to use a fragment shader as the material in my mesh.

I'm having a hard time switching from tutorials to the Three.js environment, because things are undefined. My problem seems to be the position variable not being set as a uniform, but I'm unsure how to do this.

Console error: .WebGLProgram: shader error: 0 gl.VALIDATE_STATUS true gl.getProgramInfoLog Varying 'position' has static-use in the frag shader, but is undeclared in the vert shader.

Shader:

<script type="x-shader/x-fragment" id="static">
        uniform float time;
        varying vec2 position;

        float rand(vec2 co){
            return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
        }

        void main()
        {
            vec2 uv = position.xy;
            float r = floor(rand(uv * time / 2.0) + 0.5);
            gl_FragColor = vec4(r, r, r, 1.0);
        }
</script>

JS:

var camera, scene, renderer, geometry, material, mesh, uniforms;

init();
animate();

function init() {

    scene = new THREE.Scene();

    camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000);
    camera.position.z = 500;
    scene.add(camera);

    geometry = new THREE.CylinderGeometry(100, 100, 20, 32);

    debugMaterial = new THREE.MeshNormalMaterial();

    uniforms = {
      time: { type: 'f', value: 0 }
    };

    var shaderMaterial = new THREE.ShaderMaterial({
      uniforms: uniforms,
      fragmentShader: document.getElementById('static').innerHTML
    });

    mesh = new THREE.Mesh(geometry, shaderMaterial);
    /* toggle the mesh variable to see the object */
    //mesh = new THREE.Mesh(geometry, debugMaterial);

    scene.add(mesh);

    renderer = new THREE.CanvasRenderer();
    renderer.setSize(window.innerWidth, window.innerHeight);

    document.body.appendChild(renderer.domElement);

}

function animate() {

    requestAnimationFrame(animate);
    render();

}

function render(delta) {

    mesh.rotation.x += 0.01;
    mesh.rotation.y += 0.02;

    uniforms.time += delta * 10;

    renderer.render(scene, camera);

}

render();

Desired effect: The below pixel shader on the cylinder geometry. static

Fiddle (output is blank, although I've included a debugMaterial, so you just toggle the mesh = lines to see the geometry as intended): https://jsfiddle.net/z688644L/2/

2
  • As usual, I seemed to have fixed it within minutes of posting this \: I used gl_FragCoord.xy in place of position, and the time uniform value was NaN, so I made sure it was always a number. Commented Jan 11, 2016 at 17:55
  • You can test shaders on 3d objects in shaderfrog shaderfrog.com/app/view/7 and export them directly to three.js. Useful for saving/debugging your game effects. Commented Jan 11, 2016 at 18:25

1 Answer 1

3

It looks like you have no vertex shader. By declaring position as varying, you're telling the system that that vector comes from the vertex shader. You need a simple vertex shader that sets a variable (you'll have to rename it since position is already defined in vertex shaders) so that your fragment shader can pick it up:

<script id="vertShader" type="shader">
varying vec2 vPosition;
void main() {
    vPosition = position.xy;
    gl_Position = projectionMatrix *
                  modelViewMatrix * vec4(position, 1.0 );
}
</script>

I've renamed position to vPosition, so adjust the fragment shader accordingly. Also, I haven't tested this, so caveat emptor. It definitely doesn't handle lights or anything like that. It's just something to get you started.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.