大家好,欢迎来到IT知识分享网。
OpenGL基础知识介绍
OpenGL简介
OpenGL (全写Open Graphics Library)是指定义了一个跨编程语言、跨平台的编程接口规格的专业的图形程序接口。它用于三维图像(二维的亦可),是一个功能强大,调用方便的底层图形库。OpenGL在不同的平台上有不同的实现,但是它定义好了专业的程序接口,不同的平台都是遵照该接口来进行实现的,思想完全相同,方法名也是一致的,所以使用时也基本一致,只需要根据不同的语言环境稍有不同而已。OpenGL这套3D图形API从1992年发布的1.0版本到目前最新2014年发布的4.5版本,在众多平台上多有着广泛的使用
OpenGL 专业词解析
1.OpenGL上下文[context]
2.渲染
3.顶点数组和顶点缓冲区
画图一般是先画好骨架,然后再往骨架里面填充颜色,这对与OpenGL也是一样的,顶点数据就是要画的图像的骨架,和现实中不同的是:OpenGL中的图像都是由图元组成的,在OpenGL ES中,有3种类型的图元:点,线,三角形,在那些顶点数据最终存储在哪里呢?开发者可以选择设定的函数指针,再调用绘制方法的时候,直接由内存传入顶点数据,也就是说这部分数据之前是存储在内存当中,被称为顶点数组,而性能更高的做法是提前分配一块显存,将顶点数据预先传入当中,这部分显存,就被成为顶点缓冲区。顶点指的是我们在绘制一个图形时,它的顶点位置数据,而这个数据可以直接存储在数组中或者将其缓存到GPU内存中。
4.着色器程序Shader
OpenGL在处理shader时,和其他编译器一样,通过编译,链接等步骤,生成了着色器程序(glProgram),着色器程序同时包含顶点着色器和片段着色器的运算逻辑,在OpenGL进行绘制的时候,首先有顶点着色器对传入的顶点数据进行运算,再通过图元装配,将顶点装换为图元,然后进行光栅化,将图元这种矢量图形,转化为删格化数据,最后,将删格化数据传入片段着色器进行运算,片段着色器会对删格化数据中的每一个像素进行运算,并决定像素的颜色;
5.顶点着色器(VertexShader)
6.几何着色器(GeometryShader)
在顶点和片段着色器之间有一个可选的几何着色器(Geometry Shader),几何着色器的输入是一个图元(如点或三角形)的一组顶点。几何着色器可以在顶点发送到下一着色器阶段之前对它们随意变换。(爆炸效果)
7.片元/片段/像素着色器(FragmentShader)
8.光栅化
9.纹理
10.深度测试
11.混合
在测试阶段之后,如果像素依然没有被剔除,那么像素的颜色将会和帧缓冲区中颜色附着上颜色进行混合,混合的算法可以通过OpenGL的函数进行指定,但是OpenGL提供的混合算法有限,如果需要更加复杂的混合算法,一般可以通过像素着色器进行实现,当然性能会比原生的混合算法要差一些。混合是实现物体透明度的一种技术。就是说一个物体的颜色是本身的颜色和它背后其它物体的颜色的不同强度混合
12.GLSL
- 标量:标量表示的是只有大小没有方向的量,在GLSL中标量只有bool、int和float三种。对于int,和C一样,可以写为十进制(16)、八进制(020)或者十六进制(0x10)对于标量的运算,我们最需要注意的是精度,防止溢出问题
- 向量:向量我们可以看做是数组,在GLSL通常用于储存颜色、坐标等数据,针对维数,可分为二维、三维和四位向量。针对存储的标量类型,可以分为bool、int和float。共有vec2、vec3、vec4,ivec2、ivec3、ivec4、bvec2、bvec3和bvec4九种类型,数组代表维数、i表示int类型、b表示bool类型。
作为颜色向量时,用rgba表示分量,就如同取数组的中具体数据的索引值。三维颜色向量就用rgb表示分量。比如对于颜色向量vec4 color,color[0]和color.r都表示color向量的第一个值,也就是红色的分量。其他相同。
作为位置向量时,用xyzw表示分量,xyz分别表示xyz坐标,w表示向量的模。三维坐标向量为xyz表示分量,二维向量为xy表示分量。
作为纹理向量时,用stpq表示分量,三维用stp表示分量,二维用st表示分量。 - 矩阵:在GLSL中矩阵拥有22、33、4*4三种类型的矩阵,分别用mat2、mat3、mat4表示。我们可以把矩阵看做是一个二维数组,也可以用二维数组下表的方式取里面具体位置的值
- 采样器:采样器是专门用来对纹理进行采样工作的,在GLSL中一般来说,一个采样器变量表示一副或者一套纹理贴图。所谓的纹理贴图可以理解为我们看到的物体上的皮肤
- 结构体:和C语言中的结构体相同,用struct来定义结构体,关于结构体参考C语言中的结构体。
- 数组:数组知识也和C中相同,不同的是数组声明时可以不指定大小,但是建议在不必要的情况下,还是指定大小的好
- 空类型:空类型用void表示,仅用来声明不返回任何值得函数
限定符
attribute :一般用于各个顶点各不相同的量。如顶点颜色、坐标等。
uniform:一般用于对于3D物体中所有顶点都相同的量。比如统一颜色,统一变换矩阵等。
varying:表示易变量,一般用于顶点着色器传递到片元着色器的量。
const:常量。
浮点精度
与顶点着色器不同的是,在片元着色器中使用浮点型时,必须指定浮点类型的精度,否则编译会报错。精度有三种,分别为:
lowp:低精度。8位。
mediump:中精度。10位。
highp:高精度。16位。
内建变量
顶点着色器的内建变量
输入变量:
gl_Position:顶点坐标
gl_PointSize:点的大小,没有赋值则为默认值1,通常设置绘图为点绘制才有意义。
片元着色器的内建变量
输入变量:
gl_FragCoord:当前片元相对窗口位置所处的坐标。
gl_FragFacing:bool型,表示是否为属于光栅化生成此片元的对应图元的正面。
输出变量
gl_FragColor:当前片元颜色
gl_FragData:vec4类型的数组。向其写入的信息,供渲染管线的后继过程使用。
13.相机
根据现实生活中的经历我们知道,对一个场景,随着相机的位置、拍摄出来的画面也是不相同。将相机对应于OpenGL的世界,决定相机拍摄的结果(也就是最后屏幕上展示的结果),包括相机位置、相机观察方向以及相机的UP方向。
相机位置:相机的位置是比较好理解的,就是相机在3D空间里面的坐标点。
相机观察方向:相机的观察方向,表示的是相机镜头的朝向,你可以朝前拍、朝后拍、也可以朝左朝右,或者其他的方向。
相机UP方向:相机的UP方向,可以理解为相机顶端指向的方向。比如你把相机斜着拿着,拍出来的照片就是斜着的,你倒着拿着,拍出来的就是倒着的。
Android 设置相机位置的方法:
Matrix.setLookAtM (float[] rm, //接收相机变换矩阵 int rmOffset, //变换矩阵的起始位置(偏移量) float eyeX,float eyeY, float eyeZ, //相机位置 float centerX,float centerY,float centerZ, //观测点位置 float upX,float upY,float upZ) //up向量在xyz上的分量
14.投影
Android 设置透视投影的方法:
Matrix.frustumM (float[] m, //接收透视投影的变换矩阵 int mOffset, //变换矩阵的起始位置(偏移量) float left, //相对观察点近面的左边距 float right, //相对观察点近面的右边距 float bottom, //相对观察点近面的下边距 float top, //相对观察点近面的上边距 float near, //相对观察点近面距离 float far) //相对观察点远面距离
Android 设置正交投影的方法:
Matrix.orthoM (float[] m, //接收正交投影的变换矩阵 int mOffset, //变换矩阵的起始位置(偏移量) float left, //相对观察点近面的左边距 float right, //相对观察点近面的右边距 float bottom, //相对观察点近面的下边距 float top, //相对观察点近面的上边距 float near, //相对观察点近面距离 float far) //相对观察点远面距离
Android 矩阵相乘的方法:
Matrix.multiplyMM (float[] result, //接收相乘结果 int resultOffset, //接收矩阵的起始位置(偏移量) float[] lhs, //左矩阵 int lhsOffset, //左矩阵的起始位置(偏移量) float[] rhs, //右矩阵 int rhsOffset) //右矩阵的起始位置(偏移量)
OpenGL 和 OpenGl ES 的区别
1.OpenGL ES和OpenGL 的关系
- OpenGL ES 是OpenGL 的子集,针对手机、PDA和游戏主机嵌入式设备而设计
- OpenGL ES 是从OpenGL 裁剪定制而来的,去除了glBegin/glEnd,四边形(GL_QUADS)、多边形(GL_POLYGONS)等复杂图元等许多非绝对必要的特性,剩下最核心有用的部分。可以理解成是一个在移动平台上能够支持OpenGL 最基本功能的精简规范。
3.OpenGL ES可以做什么
- 图片处理。比如图片色调转换、美颜等。
- 摄像头预览效果处理。比如美颜相机、恶搞相机等。
- 视频处理。摄像头预览效果处理可以,这个自然也不在话下了。
- 3D游戏。3D动画等.
4.OpenGL ES版本及Android支持情况
- OpenGL ES1.0是基于OpenGL 1.3的,OpenGL ES1.1是基于OpenGL 1.5的。Android 1.0和更高的版本支持这个API规范。OpenGL ES 1.x是针对固定硬件管线的。
- OpenGL ES2.0是基于OpenGL 2.0的,不兼容OpenGL ES 1.x。Android 2.2(API 8)和更高的版本支持这个API规范。OpenGL ES 2.x是针对可编程硬件管线的。
- OpenGL ES3.0的技术特性几乎完全来自OpenGL 3.x的,向下兼容OpenGL ES 2.x。Android 4.3(API 18)及更高的版本支持这个API规范。
- OpenGL ES3.1基本上可以属于OpenGL 4.x的子集,向下兼容OpenGL ES3.0/2.0。Android 5.0(API 21)和更高的版本支持这个API规范。
Android OpenGl ES 简单使用
绘制一个简单的三角形
- 在AndroidManifest.xml文件中设置使用的OpenGL ES的版本:
<uses-feature android:glEsVersion="0x00020000" android:required="true" /> // 3.0的版本为0x00030000,3.1的版本为0x00030001。
- 创建一个GLSurfaceView用来显示图形,可以自定义继承GLSurfaceView 或者直接使用GLSurfaceView。设置OpenGL ES 2.0 版本和Render
public class OneGlSurfaceView extends GLSurfaceView {
private final OneGlRenderer oneGlRenderer; public OneGlSurfaceView(Context context) {
super (context); // 设置EGLContext客户端使用OpenGL ES 2.0 版本 setEGLContextClientVersion (2); oneGlRenderer = new OneGlRenderer (context); setRenderer (oneGlRenderer); } }
- 创建一个Render,可以自定义实现GLSurfaceView.Renderer接口。图形的具体渲染工作都是在Render中完成的。
public class OneGlRenderer1 implements GLSurfaceView.Renderer {
@Override public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// 创建 } @Override public void onSurfaceChanged(GL10 gl, int width, int height) {
// 设置大小和位置 } @Override public void onDrawFrame(GL10 gl) {
// 绘制 }
- 创建两个glsl文件,用GLSL语言写一个顶点着色器 和片元着色器程序
attribute vec4 vPosition;//定义一个4维向量 顶点位置 void main() {
gl_Position = vPosition; // gl_Position Shader的内置变量,分别为顶点位置 } // attribute 变量 一般表示顶点数据,如:顶点坐标,法线,纹理坐标,顶点颜色等 // 只能在顶点着色器程序中使用
precision mediump float;//设置默认精度 中精度。10位。 精度 为float uniform vec4 vColor;// 4维向量 顶点颜色 void main() {
gl_FragColor=vColor; // gl_FragColor Shader的内置变量,片元颜色 // uniform 一般用于对于3D物体中所有顶点都相同的量。比如光源位置,统一变换矩阵等。 }
- 确定顶点坐标和颜色,因为我们三角形只是一个平面图形,三角形正对我们来呈现。所以我们把三个顶点的Z坐标都设定为0。屏幕坐标系,从屏幕中心垂直到上下左右边缘距离都为1.0
private static float triangleCoords[] = {
0.0f, 0.5f, 0.0f, // 顶点 -0.5f, -0.5f, 0.0f, // 左下 0.5f, -0.5f, 0.0f // 右下 };
// R G B A private float color[] = {
0f, 0, 1f, 1.0f}; //蓝色
- Render接口有三个方法,分别为onSurfaceCreated、onSurfaceChanged和onDrawFrame。
在onSurfaceCreated方法中,我们来创建program对象,连接顶点和片元着色器,链接program对象
@Override public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//指定刷新颜色缓冲区时所用的颜色 //需要注意的是glClearColor只起到Set的作用,并不Clear。 //glClearColor更类似与初始化,如果不做,新的绘制就会绘制在以前的上面,类似于混合,而不是覆盖 GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); triangle = new Triangle(mContext);
创建OpenGL ES 程序并链接
public Triangle(Context mContext) {
//申请底层空间 vertexBuffer = BufferUtil.floatBufferUtil (triangleCoords); // 顶点着色器 int vertexShader = OneGlRenderer.loadShader (GLES20.GL_VERTEX_SHADER, ShaderUtils.loadFromAssetsFile (vertexShaderCode,mContext.getResources ())); // 片元着色器 int fragmentShader = OneGlRenderer.loadShader (GLES20.GL_FRAGMENT_SHADER, ShaderUtils.loadFromAssetsFile (fragmentShaderCode,mContext.getResources ())); // 创建空的OpenGL ES程序 mProgram = GLES20.glCreateProgram (); // 添加顶点着色器到程序中 GLES20.glAttachShader (mProgram, vertexShader); // 添加片段着色器到程序中 GLES20.glAttachShader (mProgram, fragmentShader); // 创建OpenGL ES程序可执行文件 GLES20.glLinkProgram (mProgram); }
编译着色器代码
public static int loadShader(int type, String shaderCode) {
// 创造顶点着色器类型(GLES20.GL_VERTEX_SHADER) // 或者是片段着色器类型 (GLES20.GL_FRAGMENT_SHADER) int shader = GLES20.glCreateShader(type); // 添加上面编写的着色器代码并编译它 GLES20.glShaderSource(shader, shaderCode); GLES20.glCompileShader(shader); return shader; }
申请空间的方法
public static FloatBuffer floatBufferUtil(float[] arr) {
FloatBuffer mBuffer; // 初始化ByteBuffer,长度为arr数组的长度*4,因为一个float占4个字节 ByteBuffer qbb = ByteBuffer.allocateDirect(arr.length * 4); // 数组排列用nativeOrder qbb.order(ByteOrder.nativeOrder()); mBuffer = qbb.asFloatBuffer(); mBuffer.put(arr); mBuffer.position(0); return mBuffer; }
在onSurfaceChanged中设置设置视图窗口:
@Override public void onSurfaceChanged(GL10 gl, int width, int height) {
// glViewport用于告诉OpenGL应把渲染之后的图形绘制在窗体的哪个部位、大小 GLES20.glViewport(0, 0, width, height);
最后在onDrawFrame中绘制:
public void draw() {
// 将程序添加到OpenGL ES环境 GLES20.glUseProgram (mProgram); // 获取顶点着色器的位置的句柄 mPositionHandle = GLES20.glGetAttribLocation (mProgram, "vPosition"); // 启用三角形顶点位置的句柄 GLES20.glEnableVertexAttribArray (mPositionHandle); //准备三角形坐标数据 GLES20.glVertexAttribPointer (mPositionHandle, 3, GLES20.GL_FLOAT, false, 12, vertexBuffer); // 获取片段着色器的颜色的句柄 mColorHandle = GLES20.glGetUniformLocation (mProgram, "vColor"); // 设置绘制三角形的颜色 GLES20.glUniform4fv (mColorHandle, 1, color, 0); // 绘制三角形 GLES20.glDrawArrays (GLES20.GL_TRIANGLES, 0, vertexCount); // 禁用顶点数组 GLES20.glDisableVertexAttribArray (mPositionHandle); }
- 最终效果
- 绘制一个旋转动态的三角形(位置 和颜色可变)
修改一下着色器程序 如下:
顶点着色器
attribute vec4 vPosition;//定义一个4维向量 顶点位置 uniform mat4 uMVPMatrix;// 定义一个4x4 的变化矩阵 varying vec4 vColor;// 定义一个可变的 4维向量 传递颜色 attribute vec4 aColor;// 定义一个4维向量 赋值颜色 void main() {
gl_Position =uMVPMatrix* vPosition;// gl_Position Shader的内置变量,分别为顶点位置 vColor=aColor; // 颜色传递 }
片元着色器
precision mediump float;//中精度。10位。 精度 为float varying vec4 vColor;// 可变的 4维向量 顶点颜色 void main() {
gl_FragColor=vColor; // gl_FragColor Shader的内置变量,片元颜色 // uniform 一般用于对于3D物体中所有顶点都相同的量。比如光源位置,统一变换矩阵等。 }
- 使用变换矩阵
相机设置和投影设置并不是真正的设置,而是通过设置参数,得到一个使用相机后顶点坐标的变换矩阵,和投影下的顶点坐标变换矩阵,我们还需要把矩阵传入给顶点着色器,在顶点着色器中用传入的矩阵乘以坐标的向量,得到实际展示的坐标向量。注意,是矩阵乘以坐标向量,不是坐标向量乘以矩阵,矩阵乘法是不满足交换律的。
而通过上面的相机设置和投影设置,我们得到的是两个矩阵,为了方便,我们需要将相机矩阵和投影矩阵相乘,得到一个实际的变换矩阵,再传给顶点着色器 - 设置相机和投影,获取相机矩阵和投影矩阵,然后用相机矩阵与投影矩阵相乘,得到实际变换矩阵:
@Override public void onSurfaceChanged(GL10 gl, int width, int height) {
//计算宽高比 float ratio = (float) width / height; // 设置透视投影 偏移量 近左 近右 近下 近上 近面距离 远面距离 Matrix.frustumM(mProjectionMatrix, 0, -ratio, ratio, -1, 1, 3, 20); // 设置相机位置 Matrix.setLookAtM(mViewMatrix, 0, 5.0f, 5.0f, 10.0f, 0f, 0f, 0f, 0f, 1.0f, 0.0f); // 相乘计算变化矩阵 Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mViewMatrix, 0); }
设置一个旋转矩阵
@Override public void onDrawFrame(GL10 gl) {
// 创建一个旋转矩阵 float[] rotateMatrix = new float[16]; long time = SystemClock.uptimeMillis() % 4000L; float angle = 0.090f * ((int) time); // 旋转矩阵 Matrix.setRotateM(mRotationMatrix, 0, angle, 0, 0, -1.0f); // 将旋转矩阵与投影和相机视图组合在一起 Matrix.multiplyMM(rotateMatrix, 0, mMVPMatrix, 0, mRotationMatrix, 0); // 开始绘制 GraphicsTypeDraw(type,rotateMatrix); }
绘制
glVertexAttribPointer 参数定义 / * index * * 指定要修改的通用顶点属性的索引。 * * size * * 指定每个通用顶点属性的组件数。 必须为1,2,3或4.初始值为4。 * * type * * 指定数组中每个组件的数据类型。 接受符号常量GL_BYTE,GL_UNSIGNED_BYTE,GL_SHORT,GL_UNSIGNED_SHORT,GL_FIXED或GL_FLOAT。 初始值为GL_FLOAT。 * * normalized * * 指定在访问定点数据值时是应将其标准化(GL_TRUE)还是直接转换为定点值(GL_FALSE)。 * * stride * * 指定连续通用顶点属性之间的字节偏移量。 如果stride为0,则通用顶点属性被理解为紧密打包在数组中的。 初始值为0。 * * pointer * * 指定指向数组中第一个通用顶点属性的第一个组件的指针。 初始值为0。 */ public void draw(float[] mvpMatrix) {
// 将程序添加到OpenGL ES环境 GLES20.glUseProgram(mProgram); // 得到形状的变换矩阵的句柄 mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); // 将投影和视图转换传递给着色器 GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0); // 获取顶点着色器的位置的句柄 mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); // 启用三角形顶点位置的句柄 GLES20.glEnableVertexAttribArray(mPositionHandle); //准备三角形坐标数据 GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer); //获取片元着色器的aColor成员的句柄 mColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor"); //设置绘制三角形的颜色 GLES20.glEnableVertexAttribArray(mColorHandle); GLES20.glVertexAttribPointer(mColorHandle, 4, GLES20.GL_FLOAT, false, 0, colorBuffer); // 画三角形 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount); // 禁用顶点数组 GLES20.glDisableVertexAttribArray(mPositionHandle); }
- 展示效果
绘制正方形和正方体
- 构建正方形
在OpenGL的世界里面是没有正方形的,只有点、线、三角形。三角形就是OpenGLES提供的最复杂的图元单位。所以我们要绘制填充的正方形和圆形就需要利用三角形来实现。
正方形:正方形的构建比较简单,可以用两个三角形组成。当然,你也可以用很多很多三角形去合成一个正方形。如下图所示,我们可以按照123组成的三角形和134组成的三角形,两个拼合成一个正方形。
顶点数组
static float triangleCoords[] = {
-0.5f, 0.5f, 0.0f, // 1 -0.5f, -0.5f, 0.0f, // 2 0.5f, -0.5f, 0.0f, // 3 0.5f, 0.5f, 0.0f // 4 };
- 图形的绘制
GLES20.glDrawArrays的第一个参数表示绘制方式,第二个参数表示偏移量,第三个参数表示顶点个数。
绘制方式有:
int GL_LINES //将传入的坐标作为单独线条绘制,ABCD 4个顶点,绘制AB、CD、2条线 int GL_LINE_STRIP //将传入的顶点作为折线绘制,ABCD四个顶点,绘制AB、BC、CD三条线 int GL_LINE_LOOP //将传入的顶点作为闭合折线绘制,ABCD四个顶点,绘制AB、BC、CD、DA四条线。 int GL_TRIANGLES //将传入的顶点作为单独的三角形绘制,ABCDAC绘制ABC,DAC两个三角形 int GL_TRIANGLE_FAN //将传入的顶点作为扇面绘制,ABCD绘制ABC、ACD、2个三角形 int GL_TRIANGLE_STRIP //将传入的顶点作为三角条带绘制,ABCD绘制ABC,BCD,2个三角形
- 顶点法和索引法
上述提到的绘制,使用的都是GLES20.glDrawArrays,也就是顶点法,是根据传入的定点顺序进行绘制的。还有一个方法进行绘制GLES20.glDrawElements,称之为索引法,是根据索引序列,在顶点序列中找到对应的顶点,并根据绘制的方式,组成相应的图元进行绘制。
顶点法拥有的绘制方式,索引法也都有。相对于顶点法在复杂图形的绘制中无法避免大量顶点重复的情况,索引法可以相对顶点法减少很多重复顶点占用的空间。
索引法区别的地方:
// 索引内存buffer private ShortBuffer indexBuffer; // 此数组中每个顶点的坐标数 private static final int COORDS_PER_VERTEX = 3; static float triangleCoords[] = {
-0.5f, 0.5f, 0.0f, // 1 索引 0 -0.5f, -0.5f, 0.0f, // 2 索引 1 0.5f, -0.5f, 0.0f, // 3 索引 2 0.5f, 0.5f, 0.0f , // 4 索引 3 }; static short index[]={
0,1,2,0,2,3 };
// 初始化索引buff indexBuffer = BufferUtil.shortBufferUtil (index); //索引法绘制正方形 // glDrawElements 参数定义 // 1 指定要渲染的图元类型。 接受符号常量GL_POINTS,GL_LINE_STRIP,GL_LINE_LOOP,GL_LINES,GL_TRIANGLE_STRIP,GL_TRIANGLE_FAN和GL_TRIANGLES。 // 2 指定要渲染的元素数。 // 3 指定indices中值的类型。 必须是GL_UNSIGNED_BYTE或GL_UNSIGNED_SHORT。 // 4 指定指向存储索引的位置的指针。 GLES20.glDrawElements(GLES20.GL_TRIANGLES,index.length, GLES20.GL_UNSIGNED_SHORT,indexBuffer);
- 展示效果
- 绘制正方体
立方体是是由六个正方形组成,我们将这六个正方形绘制出来,正方体就绘制出来了。
首先确定顶点,正方体有8个顶点,我们列出8个顶点,并用索引绘制发绘制。
如图:
// 8个顶点 static float cubeCoords[] = {
-1.0f,1.0f,1.0f, //正面左上0 -1.0f,-1.0f,1.0f, //正面左下1 1.0f,-1.0f,1.0f, //正面右下2 1.0f,1.0f,1.0f, //正面右上3 -1.0f,1.0f,-1.0f, //反面左上4 -1.0f,-1.0f,-1.0f, //反面左下5 1.0f,-1.0f,-1.0f, //反面右下6 1.0f,1.0f,-1.0f, //反面右上7 }; // 6个面 static short cubeIndex[]={
0,3,2,0,2,1, //正面 0,1,5,0,5,4, //左面 0,7,3,0,4,7, //上面 6,7,4,6,4,5, //后面 6,3,7,6,2,3, //右面 6,5,1,6,1,2 //下面 }; // 8个顶点颜色 private float cubeColor[] = {
0f,0f,1f,1f, 0f,0f,1f,1f, 0f,1f,0f,1f, 0f,1f,0f,1f, 1f,0f,0f,1f, 1f,0f,0f,1f, 1f,0f,1f,1f, 1f,1f,0f,1f, };
- 注意创建程序前需要开启深度测试并在绘制前清除深度缓存,否则绘制的效果不正常
如图:
@Override public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//开启深度测试 GLES20.glEnable(GLES20.GL_DEPTH_TEST); @Override public void onDrawFrame(GL10 gl) {
// 清除深度缓存 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
Obj,STL格式3D模型加载
- 模型加载:
3D模型文件有多种格式的,每个格式的解析有所区别。文件解析得到法向量数据源,定点数据源等。
//解析二进制的Stl文件 public Model parserBinStl(InputStream in) throws IOException {
if (stlLoadListener != null) stlLoadListener.onstart(); Model model = new Model(); //前面80字节是文件头,用于存贮文件名; in.skip(80); //紧接着用 4 个字节的整数来描述模型的三角面片个数 byte[] bytes = new byte[4]; in.read(bytes);// 读取三角面片个数 int facetCount = BufferUtil.byte4ToInt(bytes, 0); model.setFacetCount(facetCount); if (facetCount == 0) {
in.close(); return model; } // 每个三角面片占用固定的50个字节 byte[] facetBytes = new byte[50 * facetCount]; // 将所有的三角面片读取到字节数组 in.read(facetBytes); //数据读取完毕后,可以把输入流关闭 in.close(); parseModel(model, facetBytes); if (stlLoadListener != null) stlLoadListener.onFinished(); return model; }
- 模型渲染
模型的渲染,和之前绘制各种形体也差不多了,为了让3D模型呈现出立体效果,示例中,增加了简的光照
//开启光照 public void openLight(GL10 gl) {
gl.glEnable(GL10.GL_LIGHTING); gl.glEnable(GL10.GL_LIGHT0); gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, BufferUtil.floatBufferUtil(ambient)); gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, BufferUtil.floatBufferUtil(diffuse)); gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_SPECULAR, BufferUtil.floatBufferUtil(specular)); gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, BufferUtil.floatBufferUtil(lightPosition)); } public void enableMaterial(GL10 gl) {
//材料对环境光的反射情况 gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT, BufferUtil.floatBufferUtil(materialAmb)); //散射光的反射情况 gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_DIFFUSE, BufferUtil.floatBufferUtil(materialDiff)); //镜面光的反射情况 gl.glMaterialfv(GL10.GL_FRONT_AND_BACK, GL10.GL_SPECULAR, BufferUtil.floatBufferUtil(materialSpec)); }
- 传入从STL文件读取的值,然后和渲染一个立方体一样,渲染出模型就OK了
- 展示效果如图:
参考博文:
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/128702.html