android - 为什么立方体的表面呈现为透明的?

标签 android opengl-es opengl-es-2.0 transparent lighting

我正在开发一个简单的程序,它在屏幕上显示一个带有照明的立方体。我在 Android 设备上使用 OpenGL ES 2.0。但只有立方体的底面是不透明的。其他表面看起来是透明的(我认为这是因为照明。当我停止使用照明时,看起来所有表面都是不透明的)以便您可以看到它后面的表面。

这是我的立方体的顶点:

    static float vertexs[] = { // in counterclockwise order:
    // front
    -0.5f, -0.5f, 0.5f,
     0.5f, -0.5f, 0.5f,
    -0.5f, 0.5f, 0.5f,
     0.5f, 0.5f, 0.5f,
    // back
    -0.5f, -0.5f, -0.5f,
     0.5f, -0.5f, -0.5f,
    -0.5f, 0.5f, -0.5f,
     0.5f, 0.5f, -0.5f,
    // left
    -0.5f, -0.5f, 0.5f,
    -0.5f, -0.5f, -0.5f,
    -0.5f, 0.5f, 0.5f,
    -0.5f, 0.5f, -0.5f,
    // right  
     0.5f, -0.5f, 0.5f,
     0.5f, -0.5f, -0.5f,
     0.5f, 0.5f, 0.5f,
     0.5f, 0.5f, -0.5f,  
    // up  
    -0.5f, 0.5f, 0.5f,
     0.5f, 0.5f, 0.5f,
    -0.5f, 0.5f, -0.5f,
     0.5f, 0.5f, -0.5f,
    // bottom  
    -0.5f, -0.5f, 0.5f,
     0.5f, -0.5f, 0.5f,
    -0.5f, -0.5f, -0.5f,
     0.5f, -0.5f, -0.5f
};

这是我正在使用的法线(是这里的问题吗?):

    static float normals[] = {
    // front
    -1.0f, -1.0f, 1.0f,
     1.0f, -1.0f, 1.0f,
    -1.0f, 1.0f, 1.0f,
     1.0f, 1.0f, 1.0f,
    // back
    -1.0f, -1.0f, -1.0f,
     1.0f, -1.0f, -1.0f,
    -1.0f, 1.0f, -1.0f,
     1.0f, 1.0f, -1.0f,
    // left
    -1.0f, -1.0f, 1.0f,
    -1.0f, -1.0f, -1.0f,
    -1.0f, 1.0f, 1.0f,
    -1.0f, 1.0f, -1.0f,
    // right  
     1.0f, -1.0f, 1.0f,
     1.0f, -1.0f, -1.0f,
     1.0f, 1.0f, 1.0f,
     1.0f, 1.0f, -1.0f,  
    // up  
    -1.0f, 1.0f, 1.0f,
     1.0f, 1.0f, 1.0f,
    -1.0f, 1.0f, -1.0f,
     1.0f, 1.0f, -1.0f,
    // bottom  
    -1.0f, -1.0f, 1.0f,
     1.0f, -1.0f, 1.0f,
    -1.0f, -1.0f, -1.0f,
     1.0f, -1.0f, -1.0f
};

着色器如下:

private final String vertexShaderCode =
        // Light
        "uniform vec4 u_LightAmbient;\n" + 
        "uniform vec4 u_LightDiffuse;\n" + 
        "uniform vec4 u_LightSpecular;\n" + 
        "uniform vec4 u_LightPos;\n" + 

        //Material
        "uniform vec4 u_MaterialAmbient;\n" + 
        "uniform vec4 u_MaterialDiffuse;\n" + 
        "uniform vec4 u_MaterialSpecular;\n" + 
        "uniform float u_MaterialShininess;\n" + 

        // Matrices
        "uniform mat4 u_ModelViewMatrix;\n" +
        "uniform mat4 u_ProjectionMatrix;\n" +
        "uniform mat4 u_NormalMatrix;\n" +

        // vertecies
        "attribute vec4 a_Position;\n" +
        "attribute vec3 a_Normal;\n" +

        "varying vec4 v_Color;\n" +

        "void main() {\n" +
            "vec4 ambient = u_LightAmbient * u_MaterialAmbient;\n" + 

            "vec3 P = vec3(u_ModelViewMatrix * a_Position);\n" + 
            "vec3 L = normalize(vec3(u_LightPos) - P);\n" + 
            "vec3 N = normalize(mat3(u_NormalMatrix) * a_Normal);\n" +
            "vec4 diffuseP = vec4(max(dot(L, N), 0.0));\n" + 
            "vec4 diffuse = diffuseP * u_LightDiffuse * u_MaterialDiffuse;\n" +

            "vec3 S = normalize(L + vec3(0.0, 0.0, 1.0));\n" + 
            "float specularP = pow(max(dot(N,S), 0.0), u_MaterialShininess);\n" +
            "vec4 specular = specularP * u_LightSpecular * u_MaterialSpecular;\n" + 

            //hyouji sareru iro
            "v_Color = ambient + specular + diffuse;\n" + 

            //position
            "gl_Position = u_ProjectionMatrix * u_ModelViewMatrix * a_Position;\n" +
        "}\n";

private final String fragmentShaderCode =
        "precision mediump float;\n" +

        "varying vec4 v_Color;\n" +

        "void main() {\n" +
            "gl_FragColor = v_Color;\n" +
        "}\n";    

绘图函数如下:

public Cube() {
    // initialize vertex byte buffer for shape coordinates
    ByteBuffer bb = ByteBuffer.allocateDirect(vertexs.length * 4);
    ByteBuffer nb = ByteBuffer.allocateDirect(normals.length * 4);

    // use the device hardware's native byte order
    bb.order(ByteOrder.nativeOrder());
    nb.order(ByteOrder.nativeOrder());

    // create a floating point buffer from the ByteBuffer
    vertexBuffer = bb.asFloatBuffer();
    normalBuffer = nb.asFloatBuffer();

    // add the coordinates to the FloatBuffer
    vertexBuffer.put(vertexs);
    float div = (float)Math.sqrt(3);
    for(int i = 0; i < normals.length; i++){
        normals[i] /= div;
    }
    normalBuffer.put(normals);

    // set the buffer to read the first coordinate
    vertexBuffer.position(0);
    normalBuffer.position(0);

    // prepare shaders and OpenGL program
    int vertexShader = MyRenderer.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
    int fragmentShader = MyRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

    mProgram = GLES20.glCreateProgram();             // create empty OpenGL Program
    GLES20.glAttachShader(mProgram, vertexShader);   // add the vertex shader to program
    GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
    GLES20.glLinkProgram(mProgram);                  // create OpenGL program executables
}

public void draw(float[] mvMatrix, float[] pMatrix) {
    // Add program to OpenGL environment
    GLES20.glUseProgram(mProgram);

    // get handle to vertex shader's aPosition member
    positionHandle = GLES20.glGetAttribLocation(mProgram, "a_Position");
    normalHandle = GLES20.glGetAttribLocation(mProgram,  "a_Normal");
    GLES20.glEnableVertexAttribArray(positionHandle);
    GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);

    GLES20.glEnableVertexAttribArray(normalHandle);
    GLES20.glVertexAttribPointer(normalHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, normalBuffer);

    modelViewMatrixHandle = GLES20.glGetUniformLocation(mProgram, "u_ModelViewMatrix");
    GLES20.glUniformMatrix4fv(modelViewMatrixHandle, 1, false, mvMatrix, 0);        

    projectionMatrixHandle = GLES20.glGetUniformLocation(mProgram, "u_ProjectionMatrix");
    GLES20.glUniformMatrix4fv(projectionMatrixHandle, 1, false, pMatrix, 0);

    normalMatrixHandle = GLES20.glGetUniformLocation(mProgram, "u_NormalMatrix");
    float[] tmp = new float[16];
    float[] normalMatrix = new float[16];
    Matrix.invertM(tmp, 0, mvMatrix, 0);
    Matrix.transposeM(normalMatrix, 0, tmp, 0);
    GLES20.glUniformMatrix4fv(normalMatrixHandle, 1, false, normalMatrix, 0);

    lightAmbientHandle = GLES20.glGetUniformLocation(mProgram, "u_LightAmbient");
    GLES20.glUniform4f(lightAmbientHandle, 0.2f, 0.2f, 0.2f, 1.0f);
    lightDiffuseHandle = GLES20.glGetUniformLocation(mProgram, "u_LightDiffuse");
    GLES20.glUniform4f(lightDiffuseHandle, 0.5f, 0.5f, 0.5f, 1.0f);
    lightSpecularHandle = GLES20.glGetUniformLocation(mProgram, "u_LightSpecular");
    GLES20.glUniform4f(lightSpecularHandle, 0.0f, 0.0f, 0.0f, 1.0f);
    lightPosHandle = GLES20.glGetUniformLocation(mProgram, "u_LightPos");

    materialAmbientHandle = GLES20.glGetUniformLocation(mProgram, "u_MaterialAmbient");
    materialDiffuseHandle = GLES20.glGetUniformLocation(mProgram, "u_MaterialDiffuse");
    materialSpecularHandle = GLES20.glGetUniformLocation(mProgram, "u_MaterialSpecular");
    materialShininessHandle = GLES20.glGetUniformLocation(mProgram, "u_MaterialShininess");

    // Front
    setMaterial(0.0f, 1.0f, 0.0f, 1.0f);
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

    // Back
    setMaterial(0.0f, 1.0f, 0.0f, 1.0f);
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 4, 4);

    // Left
    setMaterial(0.0f, 1.0f, 0.0f, 1.0f);
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 8, 4);

    // Right
    setMaterial(0.0f, 1.0f, 0.0f, 1.0f);
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 12, 4);

    // Top
    setMaterial(0.0f, 1.0f, 0.0f, 1.0f);
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 16, 4);

    // Bottom
    setMaterial(0.0f, 1.0f, 0.0f, 1.0f);
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 20, 4);

    // Disable vertex array
    GLES20.glDisableVertexAttribArray(positionHandle);
}    

private void setMaterial(float r, float g, float b, float a){
    GLES20.glUniform4f(materialAmbientHandle, r, g, b, a);

    GLES20.glUniform4f(materialDiffuseHandle, r, g, b, a);

    GLES20.glUniform4f(materialSpecularHandle, r, g, b, a);

    GLES20.glUniform4f(materialShininessHandle, r, g, b, a);
}
}

结果图如下

enter image description here

最佳答案

--- 对你的图片的额外回答---

既然您发布了图片,它看起来不像是 alpha 值。 它看起来更像背面剔除。所以您肯定为顶点设置了错误的顺序。 你知道剔除吗? 请试试这个:

GLES20.glDisable(GLES20.GL_CULL_FACE); 

它看起来比吗?

我认为问题是,你的脸被绘制在“背面”,而 Culling 是一种避免在“看不见”时绘制人脸的机制,所以如果它们的顺序错误,GL 不会渲染它们。

这可能不是网上最好的解释,但请看这里:

http://www.altdevblogaday.com/2011/08/03/backface-culling-101/

你脸上顶点的“顺序”很重要。如果你使它们顺时针或逆时针旋转,则确定 OpenGL 认为它是在渲染背面还是正面。 (参见 glFrontFace(GLenum 模式);)

enter image description here

我从上述网站获得了该图片。

如果您的着色器认为它正在渲染背面,则该面会简单地跳到您通常不需要的安全渲染资源。所以你可以看穿立方体。

你的解决方案是

  1. 禁用 backFaceCulling 所以每张脸都会被渲染 - 即使 这让一切变得更慢。
  2. glFrontFace(GLenum 模式) 反转剔除算法(你 可以设置 Gl 应该渲染正面还是背面)。但 如果你有正面和背面,这可能会导致同样的问题 立方体的侧面,所以最好的解决方案是:
  3. 检查每个面的顶点顺序。 我提到的网站,顶点的顺序需要相同。所以 检查它们并确定订单。

我认为最后一个最有效,但也是最有前途的。但如果您不确定这可能是问题所在,请先尝试禁用 BackfaceCulling。如果一切看起来都很好,您仍然可以修复它。

希望对您有所帮助。

---我原来的答案---

嗯, 通过说

gl_FragColor = v_Color;

在 fragment 着色器中和之前在顶点着色器中定义:

v_Color = ambient + specular + diffuse;

这意味着,您为 fragment/像素赋予光的值。包括阿尔法值。 所以当你用它做一些计算时,比如

vec4 diffuseP = vec4(max(dot(L, N), 0.0));

这个因子可能变得小于 1.0(向量的点积) 因为你把这个乘以你的光,每个值都可能变得小于 1.0 最后,您的光的 Alpha 值 -> 颜色变成了 1.0 到 0.0 之间的值,就像光反射本身一样。 这可能会导致透明度。

所以如果你想尝试这样的事情:

gl_FragColor = v_Color.xyz, 1.0;

或者后面纹理的alpha值,一切都会好的。

关于android - 为什么立方体的表面呈现为透明的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11864636/

相关文章:

android - ARCore – 在我面前渲染的 3D 对象

objective-c - iOS AVFoundation 将视频流式传输到 OpenGL 纹理

firefox - 为什么 Firefox 中的 gl.readPixels 给出全 0?

android - 有没有办法在 Android 上使用 OpenGL 在两个上下文/线程之间共享纹理?

android - 使用GLES20的Vertex Buffer Objects(VBO)在Android 2.3.3上不起作用

java - OpenGL ES 2.0 与 1.1

android - 无法解析方法 setSupportActionBar(androidx.appcompat.widget.Toolbar)

android - Spinner 图形错误 API 21

android - 排序顺序不适用于 listView 和游标加载器

java - 与 GC 无关的奇怪卡顿问题