Android OpenGL ES 2.0,为每个三角形计算正常

时间:2017-04-14 19:37:25

标签: java android opengl-es opengl-es-2.0

我需要在Android中使用Opengl ES 2.0为每个三角形面(不是每个顶点)计算法线。但是我无法直接在片段着色器中传递属性。

找到一个解决方案:为每个 Triangle 重复顶点,并在顶点着色器中将Triangle face Normal作为属性传递。

但我不想复制顶点。我使用 Vertex Indices 绘制三角形。

所以,一个顶点由多个三角形共享,那么我应该如何计算三角形面法线。

P.S。我是opengl的新手。

1 个答案:

答案 0 :(得分:1)

最简单的解决方案就是复制顶点。顶点着色器很少成为瓶颈。我不知道您的具体需求,但是,重复顶点不是一个好的解决方案。例如,如果对网格进行蒙皮和动画处理,则意味着在顶点着色器中进行了大量计算。另一种情况是,在顶点着色器中以某种奇怪的方式对网格进行动画处理,您必须重新计算法线。显然,您无法在顶点着色器中计算每个面法线。您可以在几何着色器中执行此操作,但我们在OpenGL ES 2.0中没有。但是,有一个简单的解决方案 - 在片段着色器中计算法线!因此,如果重复顶点对您不起作用,则解决方案如下:

  1. 我们需要一个广泛支持的OpenGL扩展 - standard_derivatives,但在运行代码之前,您仍需要检查设备是否支持它。要启用扩展程序,您必须在片段着色器的代码之前添加以下行:

    #extension GL_OES_standard_derivatives : enable
    
  2. 我们需要一个变量的变量用于世界坐标中的顶点位置。它应该在顶点着色器中计算,它的完成方式在很大程度上取决于着色器。它用于许多需求,因此您可能已经在顶点着色器中计算它。所以我们假设我们在片段着色器中有这一行:

    varying vec3 positionWorld;
    
  3. 我们需要一个相机的视图矩阵。您可能已经将一个传递给片段着色器。让我们假设我们在片段着色器中有这个统一:

    uniform mat4 viewMatrix;
    
  4. 现在,我们将计算正常情况。首先,我们在视图空间中计算法线,然后转换为世界空间。要在视图空间中计算法线,我们使用派生函数:

    vec3 normalViewSpace = normalize(cross(dFdx(positionWorldSpace), dFdy(positionWorldSpace)));
    

    这里,相对于屏幕空间中的x和y坐标获取位置的导数。这意味着我们有两个矢量位于表面平面中。为了使表面正常,我们做了一个交叉产品。当然,结果不是单位矢量,所以我们也需要对其进行标准化。

  5. 最后一步是在世界空间中计算法线。视图矩阵应用从世界空间到视图空间的转换。我们可以认为我们需要计算它的倒数,因为我们需要从视图空间到世界空间,但由于视图矩阵是正交的,因此该矩阵的转置也是它的逆,所以代码将是:

    vec3 normalWorldSpace = (vec4(normalViewSpace, 0.0) * viewMatrix).xyz;
    
  6. 为了让生活更轻松,我们可以将所有东西都包装成一个函数:

    vec3 ReconstructNormal(vec3 positionWorldSpace) 
    {
        vec3 normalViewSpace = normalize(cross(dFdx(positionWorldSpace), dFdy(positionWorldSpace)));
        vec3 normalWorldSpace = (vec4(normalViewSpace, 0.0) * viewMatrix).xyz;
        return normalWorldSpace;
    }
    
  7. 现在我们在世界空间中有一个重建的法线。下面,只是一个简单的例子,为什么这可能非常有用。请注意,由于它使用WebGL,因此它也与OpenGL ES 2.0兼容。

    
    
    var container;
    var camera, scene, renderer;
    var mesh;
    var uniforms;
    
    var clock = new THREE.Clock();
    
    init();
    animate();
    
    function init() {
        container = document.getElementById('container');
    
        camera = new THREE.PerspectiveCamera(40, window.innerWidth / window.innerHeight, 0.1, 100);
        camera.position.z = 0.6;
        camera.position.y = 0.2;
        camera.rotation.x = -0.45;
    
        scene = new THREE.Scene();
    
        var boxGeometry = new THREE.PlaneGeometry(0.75, 0.75, 32, 32);
         
        var heightMap = THREE.ImageUtils.loadTexture("data:image/png;base64,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");
             
        heightMap.wrapT = heightMap.wrapS = THREE.RepeatWrapping;
    
        uniforms = {u_time: {type: "f", value: 0.0 }, u_heightMap: {type: "t",value:heightMap} };
    
        var material = new THREE.ShaderMaterial({
        uniforms: uniforms,
            side: THREE.DoubleSide, 
            wireframe: false,
            vertexShader: document.getElementById('vertexShader').textContent,
            fragmentShader: document.getElementById('fragment_shader').textContent
        });
    
    
        mesh = new THREE.Mesh(boxGeometry, material);
        mesh.rotation.x = 3.14 / 2.0;
        scene.add(mesh);
    
        renderer = new THREE.WebGLRenderer();
        renderer.setClearColor( 0x000000, 1 );
        container.appendChild(renderer.domElement);
    
        onWindowResize();
    
        window.addEventListener('resize', onWindowResize, false);
    }
    
    function onWindowResize(event) {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
    }
    
    function animate() {
        requestAnimationFrame(animate);
        render();
    }
    
    function render() {
        var delta = clock.getDelta();
        uniforms.u_time.value += delta;
        //mesh.rotation.z += delta * 0.5;
        renderer.render(scene, camera);
    }
    
        body { margin: 0px; overflow: hidden; }
    
    <script src="https://threejs.org/build/three.min.js"></script>
    <div id="container"></div>
    
    <script id="fragment_shader" type="x-shader/x-fragment">
        #extension GL_OES_standard_derivatives : enable
        
        varying vec3 positionWorld; // position of vertex in world coordinates
        
        vec3 ReconstructNormal(vec3 positionWorldSpace) 
        {
            vec3 normalViewSpace = normalize(cross(dFdx(positionWorldSpace), dFdy(positionWorldSpace)));
            vec3 normalWorldSpace = (vec4(normalViewSpace, 0.0) * viewMatrix).xyz;
            return normalWorldSpace;
        }
    
        // Just some example of using a normal. Here we do a really simple shading
        void main( void ) 
        {
            vec3 lightDir = normalize(vec3(1.0, 1.0, 1.0));
            vec3 normal = ReconstructNormal(positionWorld);
            float diffuse = max(dot(lightDir, normal), 0.0);
            vec3 albedo = vec3(0.2, 0.4, 0.7);
            gl_FragColor = vec4(albedo * diffuse, 1.0);    
        }
    </script>
    
    <script id="vertexShader" type="x-shader/x-vertex">
        uniform lowp sampler2D u_heightMap;
        uniform float u_time;
        
        varying vec3 positionWorld;
                    
        // Example of vertex shader that moves vertices
        void main()
        {
            vec3 pos = position;
            vec2 offset1 = vec2(1.0, 0.5) * u_time * 0.01;
            vec2 offset2 = vec2(0.5, 1.0) * u_time * 0.01;
            float hight1 = texture2D(u_heightMap, uv + offset1).r * 0.02;
            float hight2 = texture2D(u_heightMap, uv + offset2).r * 0.02;
            pos.z += hight1 + hight2; 
            vec4 mvPosition = modelViewMatrix * vec4( pos, 1.0 );
            positionWorld = mvPosition.xyz;
            gl_Position = projectionMatrix * mvPosition;
        }
    </script>
    &#13;
    &#13;
    &#13;