Frederic Perron
Frederic Perron

Reputation: 798

Render a shader without having to add a Geometry to the scene in ThreeJS

I'm am currently trying to draw shapes or at least the equivalent of geometry in ThreeJS r128 with exclusively with shaders.

The common way to draw stuff on the screen with this library consist of creating a mesh with a geometry (here is the boilerplate code for project creation) associated to it. After the creation of that object, we can apply a shader to this new component via the ShaderMaterial class.

However, I can't find how to render the shader on the scene rather than on an object. With OpenGL Shading Language (GLSL), we can actually draw shapes on the screen without having vertices. That's what I am aiming for, but it seems that I am tangled up in this rendering system.

Is it even possible to render only with the shaders in ThreeJS ?


For testing, here is a function to create a filled circle with shader:

Fragment Shader:

uniform vec2 u_resolution;

float circleShape(vec2 position, float radius){
    return step(radius, length(position));
}

void main(){
    vec2 position = gl_FragCoord.xy / u_resolution;
    float circle = circleShape(position, 0.3);
    vec3 color = vec3(circle);
    gl_FragColor = vec4(color, 1.0);
} 

Here is my Vertex Shader too just in case:

void main(){
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

Upvotes: 1

Views: 1024

Answers (1)

Rabbid76
Rabbid76

Reputation: 211166

Just draw a THREE.PlaneGeometry on the entire canvas:

var container, camera, scene, renderer, uniforms;

init();
animate();

function init() {
    container = document.getElementById( 'container' );
    camera = new THREE.Camera();
    camera.position.z = 1;
    scene = new THREE.Scene();
    var geometry = new THREE.PlaneGeometry( 2, 2 );

    uniforms = {
        //u_time: { type: "f", value: 1.0 },
        u_resolution: { type: "v2", value: new THREE.Vector2() },
        //u_mouse: { type: "v2", value: new THREE.Vector2() }
    };

    var material = new THREE.ShaderMaterial( {
        uniforms: uniforms,
        vertexShader: document.getElementById( 'vertexShader' ).textContent,
        fragmentShader: document.getElementById( 'fragmentShader' ).textContent
    } );

    var mesh = new THREE.Mesh( geometry, material );
    scene.add( mesh );

    renderer = new THREE.WebGLRenderer();
    renderer.setPixelRatio( window.devicePixelRatio );
    container.appendChild( renderer.domElement );
    onWindowResize();
    window.addEventListener( 'resize', onWindowResize, false );
    document.onmousemove = function(e){
        //uniforms.u_mouse.value.x = e.pageX
        //uniforms.u_mouse.value.y = e.pageY
    }
}

function onWindowResize( event ) {
    renderer.setSize( window.innerWidth, window.innerHeight );
    uniforms.u_resolution.value.x = renderer.domElement.width;
    uniforms.u_resolution.value.y = renderer.domElement.height;
}

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

function render(delta_ms) {
    //uniforms.u_time.value = delta_ms;
    renderer.render( scene, camera );
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.js"></script>
<div id="container"></div>

<script id="vertexShader" type="x-shader/x-vertex">
void main() {
    gl_Position = vec4( position, 1.0 );
}
</script>

<script id="fragmentShader" type="x-shader/x-fragment">
precision mediump float;

uniform vec2 u_resolution;

float circleShape(vec2 position, float radius){
    return step(radius, length(position));
}

void main(){
    vec2 position = gl_FragCoord.xy / u_resolution;
    float circle = circleShape(position, 0.3);
    vec3 color = vec3(circle);
    gl_FragColor = vec4(color, 1.0);
} 
</script>

Upvotes: 2

Related Questions