OpenGL ES初探(四) -- 用OpenGL画正四面体,正方体,球

本文介绍如何使用OpenGL ES进行立体图形绘制,包括正四面体、正方体和球体的实现。通过构建BaseShape和ShapeProgram类,实现了图形的绘制和旋转,并通过透视投影增强3D效果。此外,还添加了触摸事件以自由旋转图形,并通过背面裁剪消除背面颜色干扰。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

OpenGL ES初探(四) – 用OpenGL画正四面体,正方体,球

目录

准备工作

添加初始代码

  1. 构建包com.yxf.variousshape3d

  2. 将上篇博客的源码1中的MainActivityCommonUtils还有Point复制到com.yxf.variousshape3d包下;将res/raw/中的着色器代码复制过来.

  3. com.yxf.variousshape3d下添加MyRender类如下

package com.yxf.variousshapes3d;

import android.content.Context;
import android.opengl.GLSurfaceView;


import com.yxf.variousshapes3d.programs.Program;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import static android.opengl.GLES20.GL_BLEND;
import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;
import static android.opengl.GLES20.GL_ONE_MINUS_SRC_ALPHA;
import static android.opengl.GLES20.GL_SRC_ALPHA;
import static android.opengl.GLES20.glBlendFunc;
import static android.opengl.GLES20.glClear;
import static android.opengl.GLES20.glClearColor;
import static android.opengl.GLES20.glEnable;
import static android.opengl.GLES20.glViewport;

public class MyRenderer implements GLSurfaceView.Renderer {
   
   

    private Context context;

    private List<Program> programs = new CopyOnWriteArrayList<Program>();

    public MyRenderer(Context context) {
        this.context = context;
    }

    public void addProgram(Program program) {
        programs.add(program);
    }

    public void clearProgram() {
        programs.clear();
    }


    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        glClearColor(1f, 1f, 1f, 0f);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        for (Program program : programs) {
            program.onSurfaceCreated(context);
        }
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        glViewport(0, 0, width, height);
        for (Program program : programs) {
            program.onSurfaceChanged(width, height);
        }
    }


    @Override
    public void onDrawFrame(GL10 gl) {
        glClear(GL_COLOR_BUFFER_BIT);
        for (Program program : programs) {
            program.onDrawFrame();
        }
    }

}

  1. com.yxf.variousshapes3d包下创建包programs,然后在programs包下添加Program类如下
package com.yxf.variousshapes3d.programs;

import android.content.Context;


import com.yxf.variousshapes3d.CommonUtils;

import static android.opengl.GLES20.GL_BLEND;
import static android.opengl.GLES20.GL_ONE_MINUS_SRC_ALPHA;
import static android.opengl.GLES20.GL_SRC_ALPHA;
import static android.opengl.GLES20.glBlendFunc;
import static android.opengl.GLES20.glEnable;
import static android.opengl.GLES20.glUseProgram;

public abstract class Program {
   
   

    protected int program;

    private int vertexResourceId, fragmentResourceId;

    private ShaderCallback shaderCallback;

    public Program() {

    }

    public void onSurfaceCreated(Context context) {
        ShaderCallback callback = getShaderCallback();
        this.vertexResourceId = callback.getVertexResourceId();
        this.fragmentResourceId = callback.getFragmentResourceId();
        shaderCallback = callback;
        if (callback == null) {
            throw new RuntimeException("the shader callback of program can not is null , program : " + getClass().getName());
        }
        String vertexShaderSource = CommonUtils.readTextFromResource(context, vertexResourceId);
        String fragmentShaderSource = CommonUtils.readTextFromResource(context, fragmentResourceId);
        int vertexShader = CommonUtils.compileVertexShader(vertexShaderSource);
        int fragmentShader = CommonUtils.compileFragmentShader(fragmentShaderSource);
        program = CommonUtils.linkProgram(vertexShader, fragmentShader);
        shaderCallback.initialize(program);
    }

    public void onSurfaceChanged(int width, int height) {

    }


    protected void useProgram() {
        glUseProgram(program);
    }

    public void onDrawFrame() {
        useProgram();
        shaderCallback.prepareDraw(program);
    }

    public abstract ShaderCallback getShaderCallback();

    interface ShaderCallback {

        int getVertexResourceId();

        int getFragmentResourceId();

        void initialize(int program);

        void prepareDraw(int program);
    }
}
结构说明

这次结构和上次结构差异还是有点大的

这次我们基于OpenGL的program扩展出来Program类

然后以Program为单位,在MyRenderer中draw出每个program

构建ShapeProgram

构思

为了避免太多的重复工作,我们不需要每个立体图形都构建一个Program类,我们可以先构建ShapeProgram类和BaseShape类,ShapeProgram负责构建场景和处理矩阵,BaseShape类负责绘制图形,然后将BaseShape添加进ShapeProgram中达到解耦和简化的目的.

构建BaseShape

我们在com.yxf.variousshapes3d包下添加shapes包,然后在shapes包下添加BaseShape类如下

package com.yxf.variousshapes3d.shapes;


import com.yxf.variousshapes3d.Point;

import java.nio.FloatBuffer;

import static android.opengl.GLES20.GL_FLOAT;
import static android.opengl.GLES20.glVertexAttribPointer;

public abstract class BaseShape {
   
   
    public static final int BYTES_PER_FLOAT = 4;

    public static final int POSITION_COMPONENT_COUNT = 3;
    public static final int COLOR_COMPONENT_COUNT = 4;
    public static final int STRIDE = (POSITION_COMPONENT_COUNT +
            COLOR_COMPONENT_COUNT) * BYTES_PER_FLOAT;
    protected int aPositionLocation;
    protected int aColorLocation;

    protected Point center;
    protected Object mLock = new Object();

    public BaseShape(Point center) {
        this.center = center;
    }

    public void setLocation(int aPositionLocation, int aColorLocation) {
        this.aPositionLocation = aPositionLocation;
        this.aColorLocation = aColorLocation;
    }


    public final void draw() {
        preDraw();
        synchronized (mLock) {
            FloatBuffer vertexData = getVertexData();
            vertexData.position(0);
            glVertexAttribPointer(aPositionLocation, POSITION_COMPONENT_COUNT, GL_FLOAT,
                    false, STRIDE, vertexData);
            vertexData.position(POSITION_COMPONENT_COUNT);
            glVertexAttribPointer(aColorLocation, COLOR_COMPONENT_COUNT, GL_FLOAT,
                    false, STRIDE, vertexData);
            drawArrays();
        }
        afterDraw();
    }

    public void initialize() {
        synchronized (mLock) {
            initWithoutLock();
        }
    }

    public static FloatBuffer encodeVertices(float[] vertices) {
        FloatBuffer vertexData = ByteBuffer.allocateDirect(vertices.length * BYTES_PER_FLOAT)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        vertexData.put(vertices);
        return vertexData;
    }

    public abstract void initWithoutLock();

    protected abstract FloatBuffer getVertexData();

    protected abstract void drawArrays();

    protected abstract void preDraw();

    protected abstract void afterDraw();

}

由于OpenGL在Android中其实是有个渲染线程的,所以在其中数据处理的地方加了锁.

构建ShapeProgram实例

我们再com.yxf.variousshapes3d.programs包下创建ShapeProgram类,继承于Program类如下

package com.yxf.variousshapes3d.programs;



import com.yxf.variousshapes3d.R;
import com.yxf.variousshapes3d.shapes.BaseShape;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import static android.opengl.GLES20.glEnableVertexAttribArray;
import static android.opengl.GLES20.glGetAttribLocation;
import static android.opengl.GLES20.glGetUniformLocation;
import static android.opengl.GLES20.glUniformMatrix4fv;
import 
#include #include "stdafx.h" #define GLUT_DISABLE_ATEXIT_HACK #include //#pragma comment(lib, "glut32.lib") GLfloat AngleX;//旋转向量 GLfloat AngleY; void display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); //这个函数其实就是对接下来要做什么进行一下声明 GL_MODELVIEW 模型视图 glLoadIdentity(); glPushMatrix();// 提供了相应的接口 { glRotatef(AngleX, 1.0f, 0.0f, 0.0f); glRotatef(AngleY, 0.0f, 1.0f, 0.0f); glBegin(GL_POLYGON); //前表面 glColor3f(1.0f,1.0f,1.0f);//颜色设置为白色 glVertex3f(50.0f, 50.0f, 50.0f); glColor3f(1.0f,1.0f,0.0f);//颜色设置为黄色 glVertex3f(50.0f, -50.0f, 50.0f); glColor3f(1.0f,0.0f,0.0f);//颜色设置为红色 glVertex3f(-50.0f, -50.0f, 50.0f); glColor3f(1.0f,0.0f,1.0f);//颜色设置为品红色 glVertex3f(-50.0f, 50.0f, 50.0f); glEnd(); glBegin(GL_POLYGON); //后表面 glColor3f(0.0f, 1.0f, 1.0f);//颜色设置为青色 glVertex3f(50.0f, 50.0f, -50.0f); glColor3f(0.0f, 1.0f, 0.0f);//颜色设置为绿色 glVertex3f(50.0f, -50.0f, -50.0f); glColor3f(0.0f, 0.0f, 0.0f);//颜色设置为黑色 glVertex3f(-50.0f, -50.0f, -50.0f); glColor3f(0.0f, 0.0f, 1.0f);//颜色设置为蓝色 glVertex3f(-50.0f, 50.0f, -50.0f); glEnd(); glBegin(GL_POLYGON); //右表面 glColor3ub((GLubyte)255, (GLubyte)255, (GLubyte)255);//颜色设置为白色 glVertex3f(50.0f, 50.0f, 50.0f); glColor3f(0.0f, 1.0f, 1.0f);//颜色设置为青色 glVertex3f(50.0f, 50.0f, -50.0f); glColor3f(0.0f, 1.0f, 0.0f);//颜色设置为绿色 glVertex3f(50.0f, -50.0f, -50.0f); glColor3ub((GLubyte)255, (GLubyte)255, (GLubyte)0);//颜色设置为黄色 glVertex3f(50.0f, -50.0f, 50.0f); glEnd(); glBegin(GL_POLYGON); //左表面 glColor3d(0.0, 0.0, 1.0);//颜色设置为蓝色 glVertex3f(-50.0f, 50.0f, -50.0f); glColor3f(0.0f, 0.0f, 0.0f);//颜色设置为黑色 glVertex3f(-50.0f, -50.0f, -50.0f); glColor3ub((GLubyte)255, (GLubyte)0, (GLubyte)0);//颜色设置为红色 glVertex3f(-50.0f, -50.0f, 50.0f); glColor3f(1.0f, 0.0f, 1.0f);//颜色设置为品红色 glVertex3f(-50.0f, 50.0f, 50.0f); glEnd(); glBegin(GL_POLYGON); //上表面 glColor3d(0.0, 1.0, 1.0);//颜色设置为青色 glVertex3f(50.0f, 50.0f, -50.0f); glColor3d(1.0, 1.0, 1.0);//颜色设置为白色 glVertex3f(50.0f, 50.0f, 50.0f); glColor3d(1.0, 0.0, 1.0);//颜色设置为品红色 glVertex3f(-50.0f, 50.0f, 50.0f); glColor3d(0.0, 0.0, 1.0);//颜色设置为蓝色 glVertex3f(-50.0f, 50.0f, -50.0f); glEnd(); glBegin(GL_POLYGON); //下表面 glColor3f(0.0f, 1.0f, 0.0f);//颜色设置为绿色 glVertex3f(50.0f, -50.0f, -50.0f); glColor3ub((GLubyte)255, (GLubyte)255, (GLubyte)0);//颜色设置为黄色 glVertex3f(50.0f, -50.0f, 50.0f); glColor3f(1.0f, 0.0f, 0.0f);//颜色设置为红色 glVertex3f(-50.0f, -50.0f, 50.0f); glColor3f(0.0f, 0.0f, 0.0f);//颜色设置为黑色 glVertex3f(-50.0f, -50.0f, -50.0f); glEnd(); } glPopMatrix(); glutSwapBuffers(); } void reshape(int w, int h) { GLfloat aspect = (GLfloat)w / (GLfloat)h; GLfloat nRange = 100.0f; glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); //将当前矩阵指定为投影模式 glLoadIdentity(); //设置三维投影区 if (w 355.0f) { AngleX = 0.0f; } if (AngleX 355.0f) AngleY = 0.0f; if (AngleY < 0.0f) { AngleY = 355.0f; } glutPostRedisplay(); } void init() { AngleX = 45.0f; AngleY = 315.0f; glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glEnable(GL_DEPTH_TEST); //初始化OpenGL glEnable(GL_DITHER); //抖动是激活的。 glShadeModel(GL_SMOOTH);//两点间颜色有过渡效果 } void main(int argc, char* argv[]) { glutInit(&argc;, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); //使用双缓存 使用深度缓存。 glutInitWindowSize(480, 480); glutCreateWindow("OpenGL颜色立方体"); glutReshapeFunc(reshape); //窗口改变的时候调用的函数 glutDisplayFunc(display); glutSpecialFunc(key_board); //函数注册鼠标响应事件 init(); glutMainLoop(); }
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值