着色器材质透明度

Posted

技术标签:

【中文标题】着色器材质透明度【英文标题】:ShaderMaterial transparency 【发布时间】:2015-02-16 13:44:24 【问题描述】:

我在我的应用程序中使用了自定义着色器材质。它是THREE.ShaderLib['phong'] 的扩展(并重命名)副本,效果很好,除了透明度,这是命中或错过。有时透明对象后面的对象是可见的,但通常会被剔除。为了确保我没有破坏某些东西,我在下面创建了小提琴。

当场景初始化时,一切看起来都正常——三个透明平面,所有透明都正常工作。乍一看,即使移动场景似乎也能正常工作。但突然间,你可能会注意到一些爆裂声。放慢旋转速度表明,在某些角度,这些平面只是不再相互透明。 (我添加了一个方便的“BREAK IT”按钮来将相机移动到一个这样的位置。)

那么发生了什么?我还在/实际上在破坏什么吗?我已经尝试单独初始化材料,但克隆似乎也可以完成克隆材料的工作。或者这只是这种材料工作原理的一个陷阱,我必须找到另一种方法来处理它?

小提琴:http://jsfiddle.net/TheJim01/rwt64fgd/

JS:

// BufferGeometry Tester

var hostDiv, scene, renderer, camera, root, controls, light;

var WIDTH = 500;//window.innerWidth,
    HEIGHT = 500;//window.innerHeight,
    FOV = 35,
    NEAR = 1,
    FAR = 1000;

function breakIt()
    camera.position.set(-25.759449580212017, -17.239852126859287, 39.2331270225625);
    camera.lookAt(scene.position);
    camera.up.set(0.07701484672816412, 0.8931831414880415, 0.44304919494903006);


function createBufferGeometryMesh()
    var geo = new THREE.BufferGeometry();

    var tl = new THREE.Vector3(-10.,  10., 0.),
        tr = new THREE.Vector3(10.,  10., 0.),
        bl = new THREE.Vector3(-10.,  -10., 0.),
        br = new THREE.Vector3(10.,  -10., 0.);

    var n0 = new THREE.Vector3(0., 0., 1.);

    var vertices = 
        [
            tl.x, tl.y, tl.z,
            tr.x, tr.y, tr.z,
            br.x, br.y, br.z,
            bl.x, bl.y, bl.z
        ],
    normals =
        [
            n0.x, n0.y, n0.z,
            n0.x, n0.y, n0.z,
            n0.x, n0.y, n0.z,
            n0.x, n0.y, n0.z
        ],
    indices = [ 0, 2, 1, 0, 3, 2 ];

    geo.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( vertices ), 3 ) );
    geo.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( normals ), 3 ) );
    geo.addAttribute( 'index', new THREE.BufferAttribute( new Uint32Array( indices ), 1 ) );

    /*/
    var mat = new THREE.MeshPhongMaterial( 
                    color: 0xffffff,
                    ambient: 0xffffff,
                    specular: 0xffffff,
                    shininess: 50,
                    side: THREE.DoubleSide
                 );
    /*/
    var shader = THREE.ShaderLib['phong'];

    var uniforms = null,
        parameters = null;

    uniforms = THREE.UniformsUtils.clone(shader.uniforms);
    uniforms['diffuse'].value.setHex(0xcccccc);
    uniforms['ambient'].value.setHex(0x0);
    uniforms['emissive'].value.setHex(0x0);
    uniforms['specular'].value.setHex(0xffffff);
    uniforms['shininess'].value = 1;
    uniforms['opacity'].value = 0.5;
    uniforms['ambient'].value.convertGammaToLinear();

    parameters = 
        fragmentShader: shader.fragmentShader,
        vertexShader: shader.vertexShader,
        uniforms: uniforms,
        lights: true,
        fog: false,
        side: THREE.DoubleSide,
        blending: THREE.NormalBlending,
        transparent: (uniforms['opacity'].value < 1.0)
    ;
    var mat = new THREE.ShaderMaterial(parameters);

    var msh = new THREE.Mesh(geo, mat);

    return msh;


function init() 
    hostDiv = document.createElement('div');
    document.body.appendChild(hostDiv);

    scene = new THREE.Scene();

    renderer = new THREE.WebGLRenderer( antialias: true );
    renderer.setSize(WIDTH, HEIGHT);
    renderer.setClearColor( 0x888888, 1 );
    hostDiv.appendChild(renderer.domElement);

    camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
    camera.position.z = 50;
    camera.lookAt(scene.position);

    controls = new THREE.TrackballControls(camera, renderer.domElement);

    light = new THREE.PointLight(0xffffff, 1, 1000);
    light.position.copy(camera.position);

    scene.add(camera);
    scene.add(light);

    var square = createBufferGeometryMesh();
    square.material.uniforms['diffuse'].value.setHex(0xff0000);
    square.material.needsUpdate = true;
    square.translateY(5);
    square.translateX(5);
    square.translateZ(5);
    scene.add(square);

    square = createBufferGeometryMesh();
    square.material.uniforms['diffuse'].value.setHex(0x00ff00);
    square.material.needsUpdate = true;
    square.translateY(-5);
    square.translateX(-5);
    square.translateZ(-5);
    scene.add(square);

    square = createBufferGeometryMesh();
    square.material.uniforms['diffuse'].value.setHex(0x0000ff);
    square.material.needsUpdate = true;
    square.scale.set(0.5, 0.5, 0.5);
    scene.add(square);

    animate();

    var button = document.createElement('input');
    button.setAttribute('type', 'button');
    button.setAttribute('value', 'BREAK IT');
    button.addEventListener('click', breakIt);
    document.body.appendChild(button);


function render() 
    renderer.render(scene, camera);


function animate() 
    light.position.copy(camera.position);    

    requestAnimationFrame(animate);
    render();
    controls.update();


init();

【问题讨论】:

1.恭喜你做了小实验来瘦身。这是您真正了解 webgl 和 three.js 的唯一方法。 2.当你使用透明MeshPhongMaterial代替你的自定义材质时,你看到同样的问题吗? 1.我是一个实干型的学习者,所以它最适合我,并在我需要寻求帮助时帮助说明我的观点。 :) 2. 是的。我的小提琴版本 7 将 ShaderMaterial 替换为类似配置的 MeshPhongMaterial 做了更多的挖掘、尝试等。我发现***.com/questions/15994944/…(尽管基于 r57)表明透明度是基于对象位置的。对此进行了一些测试,这是真的。通过一些调整,我将相机放到了一个位置——平面以相同的方式堆叠——蓝色平面是最近的,但红色平面仍然在蓝色平面的前面。奇怪的结果。 1.因此,它不是由您的着色器引起的。简化为MeshBasicMaterial。看看您是否可以创建一个带有无法解释的结果的简单示例。 2. 你也可以谷歌“订单无关透明度”——这不是three.js 做的,顺便说一句。 MeshBasicMaterial 也是如此。但我预料到——结果并不是那么难以解释。 Alpha 混合是基于对象位置而不是片段位置完成的。这可能会导致奇怪的情况,就像我在示例中看到的那样。但由于three.js 不支持OIT(还......github.com/mrdoob/three.js/issues/4814),我想我现在不走运。这不是一个可怕的问题,我只是希望有一个解决方法。 【参考方案1】:

您看到的透明度伪影与您的特定着色器无关。 MeshBasicMaterial 也会出现同样的问题。

three.js 中透明对象的 Alpha 混合是顺序相关的

three.js 根据对象与相机的距离对对象进行排序,并按照从最远到最近的顺序渲染透明对象。见this related answer。

因此,您可能会在移动相机时看到伪影,并且排序顺序会发生变化。

有一些特定于用例的变通方法可能会有所帮助,例如通过设置 renderer.sortObjects = false 或更改材质属性来控制渲染顺序。您必须进行试验才能看到适合您的方法。

three.js r.69

【讨论】:

以上是关于着色器材质透明度的主要内容,如果未能解决你的问题,请参考以下文章

Unity3D之Material(材质着色器纹理)

three.js 着色器材质基础

Godot Shader笔记:着色器材质ShaderMaterial

材质着色器和纹理

材质着色器和纹理

材质着色器和纹理