You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android GLES32:创建108×192彩色正方形网格顶点缓冲区求助

我来给你一个完整的可参考示例,涵盖从着色器编写到最终绘制彩色网格的全流程,完全基于GLES32实现,满足你的108×192网格、位置+颜色顶点、VSync无混合的需求:

完整GLES32彩色网格实现示例

1. 编写GLSL着色器

首先需要顶点着色器处理顶点的位置和颜色数据,片段着色器负责输出最终颜色:

顶点着色器

#version 320 es
layout(location = 0) in vec2 aPosition;
layout(location = 1) in vec3 aColor;

out vec3 vColor;

void main() {
    gl_Position = vec4(aPosition, 0.0, 1.0);
    vColor = aColor;
}

片段着色器

#version 320 es
precision mediump float;

in vec3 vColor;
out vec4 fragColor;

void main() {
    fragColor = vec4(vColor, 1.0);
}

2. 生成顶点数据(位置+颜色)

我们的网格是108行×192列的正方形,每个正方形由2个三角形(6个顶点)组成,每个顶点包含2个位置分量(x,y)和3个颜色分量(r,g,b)。这里让颜色根据行列渐变,方便直观区分网格:

// 网格尺寸
private static final int GRID_ROWS = 108;
private static final int GRID_COLS = 192;
// 每个顶点的分量数:2(位置)+3(颜色)=5
private static final int VERTEX_COMPONENTS = 5;
// 每个正方形需要6个顶点(2个三角形)
private static final int VERTICES_PER_SQUARE = 6;

private float[] generateGridVertices() {
    float[] vertices = new float[GRID_ROWS * GRID_COLS * VERTICES_PER_SQUARE * VERTEX_COMPONENTS];
    
    // 归一化设备坐标范围:x从-1到1,y从-1到1
    float cellWidth = 2.0f / GRID_COLS;
    float cellHeight = 2.0f / GRID_ROWS;
    
    int index = 0;
    for (int row = 0; row < GRID_ROWS; row++) {
        for (int col = 0; col < GRID_COLS; col++) {
            // 计算当前单元格的四个顶点位置(归一化坐标)
            float x1 = -1.0f + col * cellWidth;
            float y1 = 1.0f - row * cellHeight;
            float x2 = x1 + cellWidth;
            float y2 = y1 - cellHeight;
            
            // 生成当前单元格的渐变颜色
            float r = (float) col / GRID_COLS;
            float g = (float) row / GRID_ROWS;
            float b = 0.5f + 0.5f * (float) Math.sin((row + col) * 0.1f);
            
            // 第一个三角形:(x1,y1) -> (x2,y1) -> (x1,y2)
            vertices[index++] = x1;
            vertices[index++] = y1;
            vertices[index++] = r;
            vertices[index++] = g;
            vertices[index++] = b;
            
            vertices[index++] = x2;
            vertices[index++] = y1;
            vertices[index++] = r;
            vertices[index++] = g;
            vertices[index++] = b;
            
            vertices[index++] = x1;
            vertices[index++] = y2;
            vertices[index++] = r;
            vertices[index++] = g;
            vertices[index++] = b;
            
            // 第二个三角形:(x2,y1) -> (x2,y2) -> (x1,y2)
            vertices[index++] = x2;
            vertices[index++] = y1;
            vertices[index++] = r;
            vertices[index++] = g;
            vertices[index++] = b;
            
            vertices[index++] = x2;
            vertices[index++] = y2;
            vertices[index++] = r;
            vertices[index++] = g;
            vertices[index++] = b;
            
            vertices[index++] = x1;
            vertices[index++] = y2;
            vertices[index++] = r;
            vertices[index++] = g;
            vertices[index++] = b;
        }
    }
    return vertices;
}

3. 创建顶点缓冲区与顶点数组对象(VAO)

GLES32强制要求使用VAO管理顶点属性配置,避免重复设置:

private int vaoId;
private int vboId;
private int programId;

private void setupBuffers() {
    // 1. 编译链接着色器程序(这里假设你有工具类ShaderUtils处理编译逻辑)
    programId = ShaderUtils.createProgram(vertexShaderSource, fragmentShaderSource);
    
    // 2. 创建VAO和VBO
    int[] vao = new int[1];
    GLES32.glGenVertexArrays(1, vao, 0);
    vaoId = vao[0];
    
    int[] vbo = new int[1];
    GLES32.glGenBuffers(1, vbo, 0);
    vboId = vbo[0];
    
    // 3. 绑定VAO,后续配置会存入VAO
    GLES32.glBindVertexArray(vaoId);
    
    // 4. 绑定VBO并上传顶点数据
    GLES32.glBindBuffer(GLES32.GL_ARRAY_BUFFER, vboId);
    float[] vertices = generateGridVertices();
    ByteBuffer buffer = ByteBuffer.allocateDirect(vertices.length * 4)
            .order(ByteOrder.nativeOrder());
    buffer.asFloatBuffer().put(vertices);
    buffer.position(0);
    GLES32.glBufferData(GLES32.GL_ARRAY_BUFFER, buffer.capacity(), buffer, GLES32.GL_STATIC_DRAW);
    
    // 5. 设置位置属性指针(location=0)
    int stride = VERTEX_COMPONENTS * 4; // 每个顶点的字节数:5*4=20
    GLES32.glVertexAttribPointer(0, 2, GLES32.GL_FLOAT, false, stride, 0);
    GLES32.glEnableVertexAttribArray(0);
    
    // 6. 设置颜色属性指针(location=1)
    int colorOffset = 2 * 4; // 位置占2个float,偏移8字节
    GLES32.glVertexAttribPointer(1, 3, GLES32.GL_FLOAT, false, stride, colorOffset);
    GLES32.glEnableVertexAttribArray(1);
    
    // 7. 解绑VAO和VBO(规范操作)
    GLES32.glBindVertexArray(0);
    GLES32.glBindBuffer(GLES32.GL_ARRAY_BUFFER, 0);
}

4. 绘制流程(含VSync与无混合设置)

开启VSync

在EGL配置阶段设置垂直同步,确保帧率与显示器刷新率一致:

// 假设已获取EGLDisplay、EGLConfig和窗口Surface
EGLSurface surface = eglCreateSurface(display, config, windowSurface, null);
EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, attribList);
eglMakeCurrent(display, surface, surface, context);

// 开启VSync:交换间隔设为1(每帧等待垂直同步信号)
eglSwapInterval(display, 1);

绘制逻辑

public void onDrawFrame() {
    // 清除颜色缓冲区
    GLES32.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    GLES32.glClear(GLES32.GL_COLOR_BUFFER_BIT);
    
    // 显式关闭混合(满足无混合需求)
    GLES32.glDisable(GLES32.GL_BLEND);
    
    // 使用着色器程序
    GLES32.glUseProgram(programId);
    
    // 绑定VAO
    GLES32.glBindVertexArray(vaoId);
    
    // 绘制所有顶点:总顶点数=108*192*6
    int totalVertices = GRID_ROWS * GRID_COLS * VERTICES_PER_SQUARE;
    GLES32.glDrawArrays(GLES32.GL_TRIANGLES, 0, totalVertices);
    
    // 解绑VAO
    GLES32.glBindVertexArray(0);
    
    // 交换缓冲区(VSync在此生效)
    eglSwapBuffers(display, surface);
}

关键注意事项

  • 顶点数据格式必须与着色器声明严格匹配,否则会出现渲染错乱;
  • GLES32必须使用VAO,不能像旧版本那样直接设置顶点属性;
  • 归一化坐标(-1到1)无需手动适配屏幕分辨率,GLES会自动映射到屏幕;
  • 显式关闭混合确保不会因其他代码开启混合影响效果。

内容的提问来源于stack exchange,提问作者Alex2452

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:21:50