渲染着色器而无需在 ThreeJS 中向场景添加几何体

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

我目前正在尝试仅使用着色器绘制形状或至少相当于 ThreeJS r128 中的 geometry

使用此库在屏幕上绘制内容的常用方法包括创建一个 mesh 和一个 geometry(这里是 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。

但是,我找不到如何在场景而不是对象上渲染着色器。使用 OpenGL 着色语言 (GLSL),我们实际上可以在没有顶点的情况下在屏幕上绘制形状。本来就是这么想的,不过好像被这个渲染系统给纠结了

是否可以仅使用 ThreeJS 中的着色器进行渲染?


为了测试,这里有一个用着色器创建实心圆的函数:

片段着色器:

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

这是我的顶点着色器以防万一:

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

就在整个canvas上画一个THREE.PlaneGeometry:

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/three@0.128/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>