如何正确地为我的openGL形状添加纹理?

时间:2014-07-07 18:36:19

标签: java android opengl-es textures

所以,我的问题基本上是通过我项目中的图像为我的三角形或正方形添加纹理。虽然这似乎是一个我应该能够查找和执行的基本问题,但我很难处理从我经历过的教程中应用纹理所需的所有必要代码。我从没有编译的教程中复制并粘贴了代码,因为教程的创建者遗漏了假定已经添加的代码。我尽我所能地解决了所有语法错误,确保正确添加教程遗漏的正确代码。最后,我的应用程序崩溃了一个逻辑错误,我相信在我添加的纹理代码中。我可能会遗漏几行代码,或者我可能误用了几行代码。真的,我要求的是一个简单的完整代码(不是片段),包括简单的纹理添加到一个简单的开放gl形状(如方形),以便我可以将此代码复制并粘贴到一个新项目并将其保存为将来的参考。这也可以帮助我通过比较我的代码和正确的代码来识别我的问题。

但是,无论如何我都会发布我的代码,以防你更容易修复。

SurfaceView代码:

int mProgram;

 private final String vertexShaderCode =
            "attribute vec4 vPosition;" +"uniform mat4 uMVPMatrix;"+
            "attribute vec2 a_TexCoordinate;"+"varying vec2 v_TexCoordinate;"+
            "v_TexCoordinate = a_TexCoordinate;"+
            "void main() {" +
            "  gl_Position =uMVPMatrix*vPosition;" +
            "}";

        private final String fragmentShaderCode =
            "precision mediump float;" +
            "uniform vec4 vColor;" +"uniform sampler2D u_Texture;"+
            "varying vec2 v_TexCoordinate;"+"diffuse = diffuse + 0.3;"+
            "void main() {" +
            "gl_FragColor = (v_Color * diffuse * texture2D(u_Texture, v_TexCoordinate));" +
            "}";

class MyGLSurfaceView extends GLSurfaceView {

    private MyGLRenderer myRend;

    public MyGLSurfaceView(Context context){
        super(context);

        myRend=new MyGLRenderer();
        setEGLContextClientVersion(2);
        setRenderer(myRend);
        setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    }

    float mPreviousX;
    float mPreviousY;
    private final float TOUCH_SCALE_FACTOR = 180.0f / 320;
    @Override
    public boolean onTouchEvent(MotionEvent e) {
        // MotionEvent reports input details from the touch screen
        // and other input controls. In this case, you are only
        // interested in events where the touch position changed.

        float x = e.getX();
        float y = e.getY();

        switch (e.getAction()) {
            case MotionEvent.ACTION_MOVE:

                float dx = x - mPreviousX;
                float dy = y - mPreviousY;

                // reverse direction of rotation above the mid-line
                if (y > getHeight() / 2) {
                  dx = dx * 1 ;
                }

                // reverse direction of rotation to left of the mid-line
                if (x < getWidth() / 2) {
                  dy = dy * 1 ;
                }

                myRend.mAngle += (dx + dy) * TOUCH_SCALE_FACTOR;
                requestRender();
        }

        mPreviousX = x;
        mPreviousY = y;
        return true;
    } 
}

渲染器代码:

Triangle mTriangle;
cube mCube;
float [] mViewMatrix=new float[16];
float [] mMVPMatrix=new float[16];
float [] mProjectionMatrix=new float[16];
private float[] mRotationMatrix=new float[16];
int mTextureDataHandle;


public class MyGLRenderer implements GLSurfaceView.Renderer {

    public volatile float mAngle;
    Context context;

    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        this.context=context;

        // Load the texture
        int mTextureDataHandle =loadTexture(context, R.drawable.ic_launcher);


        mTriangle = new Triangle();

        mCube=new cube();

    }

    public void onDrawFrame(GL10 gl) {
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);

        int mTextureUniformHandle = GLES20.glGetUniformLocation(mProgram, "u_Texture");
        int mTextureCoordinateHandle = GLES20.glGetAttribLocation(mProgram, "a_TexCoordinate");

        // Set the active texture unit to texture unit 0.
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);

        // Bind the texture to this unit.
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureDataHandle);

        // Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0.
        GLES20.glUniform1i(mTextureUniformHandle, 0);

        float[] scratch = new float[16];
        //long time = SystemClock.uptimeMillis() % 4000L;
        //float mAngle = 0.090f * ((int) time);

        Matrix.setRotateM(mRotationMatrix, 0, mAngle, 0, 0, -1.0f);

        Matrix.setLookAtM(mViewMatrix, 0, 5, 0, -5, -1f, 0f, 0f, 0f, 1.0f, 0.0f);
        Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
        Matrix.multiplyMM(scratch, 0, mMVPMatrix, 0, mRotationMatrix, 0);
      //mCube.draw(scratch);


        Matrix.setLookAtM(mViewMatrix, 0, 0, 0, -3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
        Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0);
        mTriangle.draw(mMVPMatrix);

    }

    public void onSurfaceChanged(GL10 unused, int width, int height) {
        GLES20.glViewport(0, 0, width, height);

        float ratio = (float) width / height;
        Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 2, 10);
    }
}

public int loadTexture(final Context context, final int resourceId)
{
    final int[] textureHandle = new int[1];

    GLES20.glGenTextures(1, textureHandle, 0);

    if (textureHandle[0] != 0)
    {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inScaled = false;   // No pre-scaling

        // Read in the resource
        final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId, options);

        // Bind to the texture in OpenGL
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);

        // Set filtering
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);

        // Load the bitmap into the bound texture.
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);

        // Recycle the bitmap, since its data has been loaded into OpenGL.
        bitmap.recycle();
    }

    if (textureHandle[0] == 0)
    {
        throw new RuntimeException("Error loading texture.");
    }

    return textureHandle[0];
}

三角代码:

public class Triangle {


    /** This will be used to pass in the texture. */
    private int mTextureUniformHandle;

    /** This will be used to pass in model texture coordinate information. */
    private int mTextureCoordinateHandle;

    /** Size of the texture coordinate data in elements. */
    private final int mTextureCoordinateDataSize = 2;

    /** This is a handle to our texture data. */
    private int mTextureDataHandle;

    private FloatBuffer vertexBuffer, colorBuff, textureBuff;
    static final int COORDS_PER_VERTEX = 3;
    float triangleCoords[] = {   // in counterclockwise order:
            0.0f,  0.622008459f, 0.0f, // top
            -0.5f, -0.311004243f, 0.0f, // bottom left
             0.5f, -0.311004243f, 0.0f  // bottom right
    };


    float color[] = { 0.63671875f, 0.76953125f, 0.22265625f, 1.0f};


    public Triangle() {

        final float[] triangleTextureCoordinateData ={
                0.0f,  -0.622008459f, 0.0f, // top
                -0.5f, 0.311004243f, 0.0f, // bottom left
                 0.5f, 0.311004243f, 0.0f  // bottom right
        };

        ByteBuffer bb = ByteBuffer.allocateDirect(
                triangleCoords.length * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(triangleCoords);
        vertexBuffer.position(0);

        ByteBuffer tb = ByteBuffer.allocateDirect(
                triangleTextureCoordinateData.length * 4);
        tb.order(ByteOrder.nativeOrder());
        textureBuff = tb.asFloatBuffer();
        textureBuff.put(triangleTextureCoordinateData);
        textureBuff.position(0);


        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

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


        public int loadShader(int type, String shaderCode){

            // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
            // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
            int shader = GLES20.glCreateShader(type);

            // add the source code to the shader and compile it
            GLES20.glShaderSource(shader, shaderCode);
            GLES20.glCompileShader(shader);

            return shader;
        }

        static final int vertexStride = COORDS_PER_VERTEX * 4;
        static final int vertexCount = 3;

        public void draw(float [] mvpMatrix) {
            GLES20.glUseProgram(mProgram);
            int mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
            GLES20.glEnableVertexAttribArray(mPositionHandle);
            GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                                         GLES20.GL_FLOAT, false,
                                         vertexStride, vertexBuffer);
            int mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
            GLES20.glUniform4fv(mColorHandle, 1, color, 0);
           int mTriangleTextureCoordinates=GLES20.glGetUniformLocation(mProgram, "v_TexCoordinate");
            GLES20.glVertexAttribPointer(mTextureCoordinateHandle, mTextureCoordinateDataSize, GLES20.GL_FLOAT, false, 
                                        0, mTriangleTextureCoordinates);
            int mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
            GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0);
            GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
            GLES20.glDisableVertexAttribArray(mPositionHandle);
        }


}

P.S。:LogCat指向来自loadTexture方法的这行代码,说NullPointerException:

  final Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resourceId, options);

我认为这与上下文有关,但我不确定。

1 个答案:

答案 0 :(得分:0)

如果代码无法正常工作,您应该在代码中添加错误检查。在编译和链接着色器程序后,使用glGetError()检查常规错误glGetShaderiv()glGetProgramiv()以检查成功/错误和日志。

浏览代码,以下是一些跳出来的内容:

您的顶点着色器无法编译。可执行语句必须在函数内,因此v_TexCoordinate = a_TexCoordinate;应在main()函数内:

void main() {
    v_TexCoordinate = a_TexCoordinate;
    gl_Position = uMVPMatrix*vPosition;
}

片段着色器的问题更严重。同样,diffuse在主函数之外分配。它也是未声明的,并且在为表达式赋值之前在表达式中使用该值。

onSurfaceCreated()中,当您指定this.context=context时,我认为这两个都引用了成员变量,因此赋值不执行任何操作,context仍然为空。如果您需要渲染器中的上下文,则必须在启动之前将其从视图中加入到渲染器中。

onDrawFrame()中,您应该清除深度缓冲区以及颜色缓冲区。除非您另有要求,否则GLSurfaceView会选择具有深度缓冲区的配置:

GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

triangleTextureCoordinateData每个顶点包含3个组件,而它应该只有2个。

您为glGetUniformLocation()致电glGetAttribLocation()而不是v_TexCoordinate。实际上,这应该是a_TexCoordinate,因为那是您的属性变量,而v_TexCoordinatevarying变量。

我打算发布这个,因为我已经完成了打字。但事后看来,这对SO来说真的不是一个好问题。将来,请询问有关具体问题的问题,而不仅仅是发布无数问题的代码。