3

请参阅最后的编辑以了解进度。

我正在尝试学习 OpenGL ES 2.0(我将在 Android 设备上进行开发)

我对顶点和片段着色器有点困惑。我理解他们的目的,但是如果我从自定义构建的类(比如“点”)构建形状并设置它的大小和颜色或应用纹理并假设两个着色器最初是在对象类的构造函数中声明和定义的,这是否意味着该类的每个实例都有自己的一对着色器?

这是我的第一个问题。我的第二个问题是,如果是这种情况(每个对象的着色器对)............这是要走的路吗?我听说拥有一对着色器并切换它的参数并不是一个好主意,因为性能原因,但如果我有 100 个大小和颜色(或纹理)相同的精灵,那么让它们都有一个具有完全相同参数的不同着色器对?

我希望我问的是正确的问题,我研究 ES 2.0 的时间不长,所以觉得它有点混乱。我目前对OpenGL的了解有限!

编辑

根据要求添加代码。

public class Dot {

    int iProgId;
    int iPosition;
    float size = 10;
    FloatBuffer vertexBuf;
    float r = 1f;
    float g = 1f;
    float b = 1f;
    float a = 1f;
    int iBaseMap;
    int texID; 
    Bitmap imgTexture;


    //Constructor
    public Dot() {

        float[] vertices = {
                        0,0,0f              
                        };

    //Create vertex shader
    String strVShader =  
              "attribute vec4 a_position;\n"+
              "void main()\n" +
              "{\n" +
              "gl_PointSize = " +size+ ";\n" +
              "gl_Position = a_position;\n"+                 
              "}";

    //Create fragment shader
    String strFShader = 
              "precision mediump float;" +
              "void main() " +
              "{" +
              "gl_FragColor = vec4(0,0,0,1);" +
              "}";
              iProgId = Utils.LoadProgram(strVShader, strFShader);
              iPosition = GLES20.glGetAttribLocation(iProgId, "a_position");

                  vertexBuf = ByteBuffer.allocateDirect(vertices.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();

                  vertexBuf.put(vertices).position(0);


        }

我的 setTexture 方法

public void setTexture(GLSurfaceView view, Bitmap imgTexture){
    this.imgTexture=imgTexture;

    //Create vertex shader
    String strVShader =  
            "attribute vec4 a_position;\n"+
            "void main()\n" +
            "{\n" +
            "gl_PointSize = " +size+ ";\n" +
            "gl_Position = a_position;\n"+                 
            "}";

    //Fragment shader
    String strFShader =
            "precision mediump float;" +
            "uniform sampler2D u_baseMap;" +
            "void main()" +
            "{" +
            "vec4 color;" +
            "color = texture2D(u_baseMap, gl_PointCoord);" +
            "gl_FragColor = color;" +
            "}";

    iProgId = Utils.LoadProgram(strVShader, strFShader);
    iBaseMap = GLES20.glGetUniformLocation(iProgId, "u_baseMap");

    GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
    GLES20.glUniform1i(iBaseMap, 0);

    texID = Utils.LoadTexture(view, imgTexture);  //See code below
}

Utils我班级的 LoadTexture() 方法:

public static int LoadTexture(GLSurfaceView view, Bitmap imgTex) {
    int textures[] = new int[1];
    try {
        GLES20.glGenTextures(1, textures, 0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_LINEAR);
        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, imgTex, 0);
    } catch
    }
    return textures[0];
}

最后是我的绘图方法:

public void drawDot(float x, float y){

        float[] vertices = {
                x,y,0f              
                };

         vertexBuf = ByteBuffer.allocateDirect(vertices.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
         vertexBuf.put(vertices).position(0);
        GLES20.glUseProgram(iProgId);
        GLES20.glVertexAttribPointer(iPosition, 3, GLES20.GL_FLOAT, false, 0, vertexBuf);
        GLES20.glEnableVertexAttribArray(iPosition);
        GLES20.glDrawArrays(GLES20.GL_POINTS, 0, 1);
    }

所以我可以创造这样的东西:

Dot dot1 = new Dot();
dot1.setSize(40);
setTexture(myBitmap); //(created earlier with BitmapFactory)
drawDot(0,0);

谢谢!

编辑1:感谢您到目前为止的回答。在进一步的研究中,似乎其他一些人也遇到了同样的问题。问题似乎是我没有在我的渲染例程中调用 glBindTexture,因此 OpenGL 只是使用它加载的最后一个纹理,我想这是有道理的。

如果我将以下内容放入我的渲染例程中:

GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 1);

它将应用第一个位图并显示它

如果我说:

GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 2);

它将应用第二个位图并显示它

所以,找个地方!但是我现在的问题是如何让我的渲染方法根据调用它的对象(渲染例程)自动知道要使用哪个位图?

再次感谢

4

2 回答 2

3

它是如何工作的(简要)

着色器只是在显卡中运行的程序。您编译并链接它们,然后您可以将一些变量传递给它们以修改顶点和片段的属性。这意味着当您调用某些绘图函数时,例如 glDrawElements 或 glDrawArrays,顶点数据(这意味着位置、纹理坐标、法线、颜色等,取决于您要发送的内容)将被发送到管道。这意味着当前加载的顶点着色器将一一获取顶点并运行其代码以应用所需的任何转换。之后,OpenGL 将应用光栅化来为当前帧生成片段。然后片段着色器将获取每个片段并相应地对其进行修改。

您始终可以卸载着色器并加载不同的着色器。如果您需要为不同的对象使用不同的着色器,您可以根据对象的着色器对对象进行分组并独立渲染它们,同时为每个组重新加载相应的着色器。

但是,有时将一些参数传递给着色器并为每个对象更改它们会更容易。例如,如果要渲染 3D 模型,可以将其拆分为子网格,每个子网格具有不同的纹理。然后,当您传递网格的顶点数据时,加载纹理并将其传递给着色器。对于下一个网格,您将传递另一个纹理,依此类推。

在现实世界中,一切都更加复杂,但我希望它对您了解它是如何工作的有所帮助。

你的例子

您正在构造函数上加载一对着色器(没有纹理),然后每次设置纹理时创建一个新着色器。我不确定这是更好的方法。

不知道 Utils.LoadShader 做了什么很难知道,但是您可以在每次调用它时记录结果。也许您第二次链接着色器时它不起作用。

如果我是你,我只会在你的点对象之外使用一对着色器。您可以将参数传递给着色器(使用 glUniform...),指示点大小、纹理等。 setTexture 函数只会绑定新纹理而不加载着色器。然后在开始时编译(在设置 GL 上下文等之后)。

当这可行时,您可以考虑每次都更改着色器,只有在确实有必要时。

于 2013-03-05T15:58:19.080 回答
2

当我发现问题所在时,我会回答自己。

将此添加到我的 drawDot 方法中:

GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texID);

texID 是对应于调用 drawDot() 方法的对象的纹理 ID。

完美运行

希望这对将来可能遇到类似问题的人有所帮助。

于 2013-03-08T18:07:32.573 回答