OpenGL ES在android上画一个正方形

Posted

技术标签:

【中文标题】OpenGL ES在android上画一个正方形【英文标题】:OpenGL ES draw a square on android 【发布时间】:2021-04-04 20:28:24 【问题描述】:

我尝试在 OpenGL ES 中为 android 绘制一个正方形,但由于某种原因,我的代码崩溃了,或者只显示了 clearColor。我敢肯定,带有渲染器和 GLSurfaceView 类的项目设置是正确的,并且从文件导入顶点和片段着色器的功能有效。这是绘制正方形的代码(构造函数在我的渲染器类的 onDrawFrame 方法的 draw 方法中调用 onSurfaceCreated):

import android.content.Context;
import android.opengl.GLES20;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

public class Square 
    int ShaderProgramID;
    private FloatBuffer vertexBuffer;
    private int vertexBufferID;
    private int vertexCount;
    private int vertexStride;
    static final int COORDS_PER_VERTEX = 3;
    static final int COLORS_PER_VERTEX = 4;
    static final int SIZE_OF_FLOAT = 4;
    static final float coords[] = 
          //x:    y:     z:            r:    g:    b:    a:
            -0.5f, 0.5f, 0.0f,         1.0f, 0.0f, 0.0f, 1.0f,
            -0.5f,-0.5f, 0.0f,         0.0f, 1.0f, 0.0f, 1.0f,
             0.5f,-0.5f, 0.0f,         0.0f, 0.0f, 1.0f, 1.0f,
            -0.5f, 0.5f, 0.0f,         1.0f, 0.0f, 0.0f, 1.0f,
             0.5f,-0.5f, 0.0f,         0.0f, 0.0f, 1.0f, 1.0f,
             0.5f, 0.5f, 0.0f,         1.0f, 1.0f, 1.0f, 1.0f,

    ;
    public Square(Context context)  
        String vertexShaderSrc = ReadFromfile("defaultVertexShader.glsl", context);
        String fragmentShaderSrc = ReadFromfile("defaultFragmentShader.glsl", context);

        int vertexID = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
        GLES20.glShaderSource(vertexID, vertexShaderSrc);
        GLES20.glCompileShader(vertexID);

        int fragmetID = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
        GLES20.glShaderSource(fragmetID, fragmentShaderSrc);
        GLES20.glCompileShader(fragmetID);

        ShaderProgramID = GLES20.glCreateProgram();
        GLES20.glAttachShader(ShaderProgramID, vertexID);
        GLES20.glAttachShader(ShaderProgramID, fragmetID);
        GLES20.glBindAttribLocation(ShaderProgramID, 0, "aPos");
        GLES20.glBindAttribLocation(ShaderProgramID, 1, "aColor");
        GLES20.glLinkProgram(ShaderProgramID);

        positionHandle = GLES20.glGetAttribLocation(ShaderProgramID, "a_Position");
        colorHandle = GLES20.glGetAttribLocation(ShaderProgramID, "a_Color");

        vertexBuffer = FloatBuffer.allocate(coords.length);
        vertexBuffer.put(coords);
        vertexBuffer.position(0);
        IntBuffer buffer = IntBuffer.allocate(1);
        GLES20.glGenBuffers(1, buffer);
        vertexBufferID = buffer.get(0);
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBufferID);
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, coords.length * 4, vertexBuffer, GLES20.GL_STATIC_DRAW);

        vertexCount = coords.length / (COORDS_PER_VERTEX + COLORS_PER_VERTEX);
        vertexStride = (COORDS_PER_VERTEX + COLORS_PER_VERTEX) * 4;
    
    private int positionHandle;
    private int colorHandle;
    public void draw() 
        GLES20.glUseProgram(ShaderProgramID);


        vertexBuffer.position(0);
        GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
        GLES20.glEnableVertexAttribArray(positionHandle);


        vertexBuffer.position(3);
        GLES20.glVertexAttribPointer(colorHandle, COLORS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
        GLES20.glEnableVertexAttribArray(colorHandle);


        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 6);
        GLES20.glDisableVertexAttribArray(positionHandle);
        GLES20.glDisableVertexAttribArray(colorHandle);

    

    public String ReadFromfile(String fileName, Context context) 
        StringBuilder ReturnString = new StringBuilder();
        InputStream fIn = null;
        InputStreamReader isr = null;
        BufferedReader input = null;
        try 
            fIn = context.getResources().getAssets()
                    .open(fileName);
            isr = new InputStreamReader(fIn);
            input = new BufferedReader(isr);
            String line = "";
            while ((line = input.readLine()) != null) 
                ReturnString.append(line + "\n");
            
         catch (Exception e) 
            e.getMessage();
         finally 
            try 
                if (isr != null)
                    isr.close();
                if (fIn != null)
                    fIn.close();
                if (input != null)
                    input.close();
             catch (Exception e2) 
                e2.getMessage();
            
        
        return ReturnString.toString();
    

这是 VertexShaderCode:

attribute vec4 aPos;
attribute vec4 aColor;

varying vec4 v_Color;

void main()

    v_Color = a_Color;
    gl_Position = a_Position;

这是 FragmentShaderCode

precision mediump float;
varying vec4 v_Color;
void main()

    gl_FragColor = v_Color;

【问题讨论】:

【参考方案1】:

在顶点着色器中:

attribute vec4 aPos;

position 是 4 分量向量,但在“vertexBuffer”中位置写为 3 分量向量。所以尝试改进着色器:

attribute vec3 aPos;

也使用了“a_Position”,但未在任何地方声明,请这样做:

gl_Position = vec4(a_Pos,1.0);

并在这里改进:

positionHandle = GLES20.glGetAttribLocation(ShaderProgramID, "a_Pos");

此代码可以显示着色器是否编译,以及错误。

int[] compiled = new int[1];
        GLES31.glGetShaderiv(shader, GLES31.GL_COMPILE_STATUS, compiled, 0);
        if (compiled[0] == 0) 
            GLES31.glDeleteShader(shader);
            throw new RuntimeException("Could not compile program: "
                    + GLES31.glGetShaderInfoLog(shader) + " | ");
        

这是准备浮动缓冲区的正确方法:

ByteBuffer bb = ByteBuffer.allocateDirect(coords.length*4);
bb.order(ByteOrder.nativeOrder());

FloatBuffer vertexBuffer = bb.asFloatBuffer();

【讨论】:

顶点现在看起来像这样:attribute vec3 aPos;属性 vec4 aColor;不同的 vec4 vColor; void main() v_Color = aColor; gl_Position = aPos; 但它仍然崩溃。我也确保在代码中的任何地方只使用 aPos 和 aColor 而不是 a_Pos 或 a_Color gl_Position = vec4(a_Pos,1.0); 我修复了所有错误,现在着色器编译成功,但应用程序仍然崩溃 哪一行崩溃了? 第 76 行:GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);【参考方案2】:

这是当前代码:

import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLES31;
import android.util.Log;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;


public class Square 
int ShaderProgramID;
private FloatBuffer vertexBuffer;
private int vertexBufferID;
private int vertexCount;
private int vertexStride;
static final int COORDS_PER_VERTEX = 3;
static final int COLORS_PER_VERTEX = 4;
static final int SIZE_OF_FLOAT = 4;
static final float coords[] = 
        //x:    y:     z:            r:    g:    b:    a:
        -0.5f, 0.5f, 0.0f,         1.0f, 0.0f, 0.0f, 1.0f,
        -0.5f,-0.5f, 0.0f,         0.0f, 1.0f, 0.0f, 1.0f,
         0.5f,-0.5f, 0.0f,         0.0f, 0.0f, 1.0f, 1.0f,
        -0.5f, 0.5f, 0.0f,         1.0f, 0.0f, 0.0f, 1.0f,
         0.5f,-0.5f, 0.0f,         0.0f, 0.0f, 1.0f, 1.0f,
         0.5f, 0.5f, 0.0f,         1.0f, 1.0f, 1.0f, 1.0f,

;
public Square(Context context)  
    String vertexShaderSrc = ReadFromfile("defaultVertexShader.glsl", context);
    String fragmentShaderSrc = ReadFromfile("defaultFragmentShader.glsl", context);

    int vertexID = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
    GLES20.glShaderSource(vertexID, vertexShaderSrc);
    GLES20.glCompileShader(vertexID);

    int fragmetID = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
    GLES20.glShaderSource(fragmetID, fragmentShaderSrc);
    GLES20.glCompileShader(fragmetID);

    ShaderProgramID = GLES20.glCreateProgram();
    GLES20.glAttachShader(ShaderProgramID, vertexID);
    GLES20.glAttachShader(ShaderProgramID, fragmetID);
    GLES20.glBindAttribLocation(ShaderProgramID, 0, "aPos");
    GLES20.glBindAttribLocation(ShaderProgramID, 1, "aColor");
    GLES20.glLinkProgram(ShaderProgramID);

    positionHandle = GLES20.glGetAttribLocation(ShaderProgramID, "aPos");
    colorHandle = GLES20.glGetAttribLocation(ShaderProgramID, "aColor");

    vertexBuffer = FloatBuffer.allocate(coords.length);
    vertexBuffer.put(coords);
    vertexBuffer.position(0);
    IntBuffer buffer = IntBuffer.allocate(1);
    GLES20.glGenBuffers(1, buffer);
    vertexBufferID = buffer.get(0);
    GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBufferID);
    GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, coords.length * 4, vertexBuffer, GLES20.GL_STATIC_DRAW);

    vertexCount = coords.length / (COORDS_PER_VERTEX + COLORS_PER_VERTEX);
    vertexStride = (COORDS_PER_VERTEX + COLORS_PER_VERTEX) * 4;

private int positionHandle;
private int colorHandle;
public void draw() 
    GLES20.glUseProgram(ShaderProgramID);

    vertexBuffer.position(0);
    GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, 0);
    GLES20.glEnableVertexAttribArray(positionHandle);


    vertexBuffer.position(3);
    GLES20.glVertexAttribPointer(colorHandle, COLORS_PER_VERTEX, GLES20.GL_FLOAT,        false, vertexStride, 3);
    GLES20.glEnableVertexAttribArray(colorHandle);


    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 6);
    GLES20.glDisableVertexAttribArray(positionHandle);
    GLES20.glDisableVertexAttribArray(colorHandle);



public String ReadFromfile(String fileName, Context context) 
    StringBuilder ReturnString = new StringBuilder();
    InputStream fIn = null;
    InputStreamReader isr = null;
    BufferedReader input = null;
    try 
        fIn = context.getResources().getAssets()
                .open(fileName);
        isr = new InputStreamReader(fIn);
        input = new BufferedReader(isr);
        String line = "";
        while ((line = input.readLine()) != null) 
            ReturnString.append(line + "\n");
        
     catch (Exception e) 
        e.getMessage();
     finally 
        try 
            if (isr != null)
                isr.close();
            if (fIn != null)
                fIn.close();
            if (input != null)
                input.close();
         catch (Exception e2) 
            e2.getMessage();
        
    
    return ReturnString.toString();


片段:

#version 310 es
precision mediump float;
in vec4 vColor;
void main()

gl_FragColor = vColor;

顶点:

#version 310 es
attribute vec3 aPos;
attribute vec4 aColor;
out vec4 vColor;

void main()

vColor = aColor;
gl_Position = vec4(aPos,1.0);

【讨论】:

以上是关于OpenGL ES在android上画一个正方形的主要内容,如果未能解决你的问题,请参考以下文章

Android OpenGL - ES 纹理出血

如何在 Android 中将 OpenCV 旋转和平移矢量与 OpenGL ES 一起使用?

Android OpenGL ES 纹理映射/绘图问题 - 倾斜图像

Opengl es Android 3D 手指触控旋转object

使用 OpenGL ES 2.0 绘制 2D 图像

OpenGL:GL_QUADS 不绘制正方形