【问题标题】:Multiple textures overlapping webGL多个纹理重叠 webGL
【发布时间】:2018-05-30 04:16:49
【问题描述】:

我有两个对象需要使用 WebGLRenderTarget 在两个不同的纹理中进行渲染。在使用着色器将两个纹理映射到一个平面然后将该平面添加到主场景之后,我在绘制更近的对象时遇到了问题。

我创建了一个 jsfiddle 来显示我的问题:https://jsfiddle.net/11qb7ay7/82/ 真正的魔法发生在这里:

void main() {
    vec4 color = texture2D(tex_sphere, vUv);
    vec4 color_cube = texture2D(tex_cube, vUv);

    gl_FragColor = vec4(color.rgb * color_cube.rgb, 1.0);
}

球体相对于相机放置在立方体的前面。当它们重叠时如何绘制球体像素而不是立方体?

为了清楚起见,我正在尝试找到一种方法来计算每个像素与相机的距离并首先渲染更近的像素

【问题讨论】:

    标签: three.js glsl webgl fragment-shader


    【解决方案1】:

    一般来说,如果tex_sphere 有 alpha 通道,您可以通过 alpha 通道混合颜色:

    void main()
    {
        vec4 color = texture2D(tex_sphere, vUv);
        vec4 color_cube = texture2D(tex_cube, vUv);
    
        vec3 mixCol  = mix(color_cube.rgb, color.rgb, color.a);
        gl_FragColor = vec4(mixCol.rgb, 1.0);
    }
    

    如果tex_sphere有黑色背景,应该省略,你必须检查tex_sphere的颜色是否不是黑色:

    void main()
    {
        vec4 color = texture2D(tex_sphere, vUv);
        vec4 color_cube = texture2D(tex_cube, vUv);
    
        vec3  test   = step(1.0/512.0, color.rgb);
        float a      = max(max(test.r, test.g), test.b);
        vec3  mixCol = mix(color_cube.rgb, color.rgb, a);
        gl_FragColor = vec4(mixCol.rgb, 1.0);
    }
    


    请注意,mix 根据 [0.0, 1.0] 范围内的浮点插值 a 在 2 个值之间进行插值。如果a 等于0.0,则返回第一个值,如果a 等于1.0,则返回第二个值。

    step 测试一个值是否小于边缘值。如果小于则返回 0.0,否则返回 1.0。


    要获得黑色背景,您必须在将球体渲染到渲染目标时设置黑色“清晰”颜色:

    function render() {
        controls.update();
    
        renderer.setClearColor(0x00);
        renderer.render(sphereScene, camera, renderTargets.sphere, true);
    
        renderer.setClearColor(0xccccff);
        renderer.render(cubeScene, camera, renderTargets.cube, true);
    
        renderer.setClearColor(0xccccff);
        renderer.render(scene, camera);
    }
    

    如果你想使用 alpha 通道,你必须在渲染到渲染目标之前设置setClearAlpha

    function render() {
        controls.update();
    
        renderer.setClearAlpha(0);
        renderer.render(sphereScene, camera, renderTargets.sphere, true);
    
        renderer.setClearAlpha(1);
        renderer.render(cubeScene, camera, renderTargets.cube, true);
    
        renderer.render(scene, camera);
    }
    

    var scene, renderer, camera, controls;
    var cubeScene, sphereScene;
    var renderTargets;
    
    init();
    animate();
    
    function init() {
        scene = new THREE.Scene();
        cubeScene = new THREE.Scene();
        sphereScene = new THREE.Scene();
        renderer = new THREE.WebGLRenderer( { antialias: false, alpha: true } );
        renderer.setClearColor(0xccccff);
        camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 4000 );
        camera.position.set(0, 0, 200);
        
        renderer.setSize( window.innerWidth, window.innerHeight );
        controls = new THREE.OrbitControls(camera, renderer.domElement);
        camera.lookAt( scene.position );
    
        var light = new THREE.HemisphereLight( 0xffffff, 0x444444 );
        scene.add( light );
    
        container = document.createElement('div');
        document.body.appendChild(container);
        container.appendChild(renderer.domElement);
        
        
        initObjects();
        initRenderTargets(window.innerWidth, window.innerHeight);
    }
    
    function onResize() {
        renderer.setSize( window.innerWidth, window.innerHeight );
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderTargets.sphere.setSize( window.innerWidth, window.innerHeight );
        renderTargets.cube.setSize( window.innerWidth, window.innerHeight );
    }
    
    function initObjects() {
    var cGeometry = new THREE.BoxGeometry( 100, 100, 100 );
    var cMaterial = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
    var cube = new THREE.Mesh( cGeometry, cMaterial );
    cube.position.z = -210;
    cube.position.y = 100;
    
    var sGeometry = new THREE.SphereGeometry( 75, 32, 32 );
    var sMaterial = new THREE.MeshBasicMaterial({
            color: 0xff0000
        });
    var sphere = new THREE.Mesh( sGeometry, sMaterial );
    sphere.position.z = -100;
    
    
    sphereScene.add( sphere );
    cubeScene.add( cube );
    }
    
    function initRenderTargets(width, height){
    		renderTargets = createRenderTargets(width, height);
        
        var uniforms = {
        	"tex_cube": { type: "t", value: renderTargets.cube.texture },
          "tex_sphere": { type: "t", value: renderTargets.sphere.texture }
        }
        
        material = new THREE.ShaderMaterial({
        	uniforms: uniforms,
          vertexShader: document.getElementById('vs_rt').textContent,
          fragmentShader: document.getElementById('fs_rt').textContent
        });
        
        
        var plane = new THREE.PlaneGeometry(width, height);
        quad = new THREE.Mesh(plane, material);
        scene.add(quad);
    }
    
    function createRenderTargets(width, height) {
        var parameters = {
            minFilter: THREE.NearestFilter,
            magFilter: THREE.NearestFilter,
        };
    
        return {
            cube: new THREE.WebGLRenderTarget( width, height, parameters ),
            sphere: new THREE.WebGLRenderTarget( width, height, parameters )
        };
    }
    
    function animate() {
        requestAnimationFrame(animate);
    
        render();
    
    }
    
    
    //------------------------------------------
    // Main rendering
    //------------------------------------------
    function render() {
    		controls.update();
            
        renderer.setClearAlpha(0);
        renderer.render(sphereScene, camera, renderTargets.sphere, true);
        
        renderer.setClearAlpha(1);
        renderer.render(cubeScene, camera, renderTargets.cube, true);
        
        renderer.render(scene, camera);
    }
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/three.js/89/three.min.js"></script>
    <script type="text/javascript" src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
    <script id="vs_rt" type="x-shader/x-vertex">
        uniform sampler2D tex_cube;
        uniform sampler2D tex_sphere;
        varying vec2 vUv;
    
        void main() {
            vUv = uv;
    
            gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
    </script>
    
    <script id="fs_rt" type="x-shader/x-fragment">
        uniform sampler2D tex_cube;
        uniform sampler2D tex_sphere;
        varying vec2 vUv;
    
        void main() {
            vec4 color = texture2D(tex_sphere, vUv);
            vec4 color_cube = texture2D(tex_cube, vUv);
    
            vec3 mixCol  = mix(color_cube.rgb, color.rgb, color.a);
            gl_FragColor = vec4(mixCol.rgb, 1.0);
        }
    </script>

    【讨论】:

    • 感谢您的回答!这是控制将哪个纹理渲染到前面的好方法;但是,我希望渲染器渲染更接近的对象。例如,如果我无法预测对象的放置位置,则使用 Alpha 通道仍会在另一个纹理之前渲染一个纹理,而不管它的位置如何。本质上,我试图找到一种方法来计算每个像素与相机的距离并首先渲染更近的像素。
    • 你可以做类似varying vec3 vWorldPosition; vWorldPosition = (modelMatrix * vec4(position,1.)).xyz; 然后在着色器中float myDistanceToCamera = length(cameraPosition - vWorldPosition);
    • 有没有办法可以在两个纹理之间拆分 wWorldPosition?这意味着我可以比较两种纹理之间的距离。当只使用一个位置时,两个纹理将返回相同的长度,不允许我绘制更接近的纹理。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多