OpenGL基础知识介绍
OpenGL (全写Open Graphics Library)是指定义了一个跨编程语言、跨平台的编程接口规格的专业的图形程序接口。它用于三维图像(二维的亦可),是一个功能强大,调用方便的底层图形库。OpenGL在不同的平台上有不同的实现,但是它定义好了专业的程序接口,不同的平台都是遵照该接口来进行实现的,思想完全相同,方法名也是一致的,所以使用时也基本一致,只需要根据不同的语言环境稍有不同而已。OpenGL这套3D图形API从1992年发布的1.0版本到目前最新2014年发布的4.5版本,在众多平台上多有着广泛的使用
(1),在应用程序调用任何OpenGL指令之前,需要首先创建一个OpenGL的上下 文,这个上下文是一个非常庞大的状态机,保存了OpenGL中的各种状态,也是OpenGL指令执行的基础;
(2),由于OpanGL上下文是一个巨大的状态机,切换上下文往往会产生较大的开销,但是不同绘制模块可能需要使用完全独立的状态管理,因此,可以在应用程序中分别创建多个不同的上下文,在不同的线程中使用不同的上下文,上下文之间共享纹理,缓冲区等资源,这样的方案,会比反复切换上下文或者大量修改渲染状态更加合理高效
OpenGL渲染的原理是将输入的3d坐标的顶点数据结合纹理信息,和各种渲染状态绘制成屏幕上的2D像素片段的过程。如图

画图一般是先画好骨架,然后再往骨架里面填充颜色,这对与OpenGL也是一样的,顶点数据就是要画的图像的骨架,和现实中不同的是:OpenGL中的图像都是由图元组成的,在OpenGL ES中,有3种类型的图元:点,线,三角形,在那些顶点数据最终存储在哪里呢?开发者可以选择设定的函数指针,再调用绘制方法的时候,直接由内存传入顶点数据,也就是说这部分数据之前是存储在内存当中,被称为顶点数组,而性能更高的做法是提前分配一块显存,将顶点数据预先传入当中,这部分显存,就被成为顶点缓冲区。顶点指的是我们在绘制一个图形时,它的顶点位置数据,而这个数据可以直接存储在数组中或者将其缓存到GPU内存中。
OpenGL在处理shader时,和其他编译器一样,通过编译,链接等步骤,生成了着色器程序(glProgram),着色器程序同时包含顶点着色器和片段着色器的运算逻辑,在OpenGL进行绘制的时候,首先有顶点着色器对传入的顶点数据进行运算,再通过图元装配,将顶点装换为图元,然后进行光栅化,将图元这种矢量图形,转化为删格化数据,最后,将删格化数据传入片段着色器进行运算,片段着色器会对删格化数据中的每一个像素进行运算,并决定像素的颜色;
一般用来处理图形每个顶点变化【旋转、平移、投影等】;
顶点着色器是OpenGL中用于计算顶点属性的程序,顶点着色器是逐顶点运算的程序,也就是说每个顶点数据都会执行一次顶点着色器,当然这是并行的,并且顶点着色器运行中无法访问其他顶点的数据
一般来说典型的需要计算的顶点属性主要包括坐标变换,逐顶点光照运算等待,顶点坐标由自身坐标装换到归一化坐标系的运算,就是在这里发生的

在顶点和片段着色器之间有一个可选的几何着色器(Geometry Shader),几何着色器的输入是一个图元(如点或三角形)的一组顶点。几何着色器可以在顶点发送到下一着色器阶段之前对它们随意变换。(爆炸效果)
一般用来处理图形中每个像素点颜色和填充;
片元着色器是OpenGL中用于计算片段(像素)颜色的程序,片段着色器逐像素运算的程序,也就是说每个像素都会执行一次片段着色器,且是并行执行

是把顶点数据转换为片元的过程,具有将图转化为一个个栅格组成的图像的作用,特点是每个元素对应的缓冲区的一个像素;
光栅化就是把顶点数据转换为片元的过程,片元中的每个元素对应于帧缓冲区的一个像素;
光栅化其实是一种将几何图元变成二维图像的过程,该过程包含了两部分:
1.决定窗口坐标中的哪些整型删格化区域被基本图元占用,
2.分配一个颜色值和一个深度值到各个区域,光栅化的过程产生的是片元;
把物体的数学描述以及与物体相关的颜色信息转换为屏幕上用于对应位置的像素以及用于填充像素的颜色,这个过程称为光栅化,这是一个将模拟信号转化为离散信号的过程;
粗略地讲:你模型的那些顶点在经过各种矩阵变换后也仅仅是顶点。而由顶点构成的三角形要在屏幕上显示出来,除了需要三个顶点的信息以外,还需要确定构成这个三角形的所有像素的信息

为了能够把图片纹理(Bitmap)映射到三角形上,我们需要指定三角形的每个顶点各自对应纹理的哪个部分。这样每个顶点就会关联着一个纹理坐标(Texture Coordinate),用来标明该从纹理图像的哪个部分采样(译注:采集片段颜色)。之后在图形的其它片段上进行片段插值(Fragment Interpolation)。
纹理坐标在x和y轴上,范围为0到1之间(注意我们使用的是2D纹理图像)。纹理坐标用浮点数来表示,范围一般从0.0到1.0,左上角坐标为(0.0,0.0),右上角坐标为(1.0,0.0),左下角坐标为(0.0,1.0),右下角坐标为(1.0,1.0),如下图所示:

左图为纹理图和纹理坐标,右图为顶点图和顶点坐标。
将纹理映射到右边的两个三角形上(也就是一个矩形),需要将纹理坐标指定到正确的顶点上,才能使纹理正确的显示,否则显示出来的纹理会无法显示,或者出现旋转、翻转、错位等情况。
将右图顶点按照V2V1V4V3传入,以三角形条带方式绘制,则纹理坐标应按照V2V1V4V3传入。如果按照V3V4V1V2传入,会得到一个旋转了180度的纹理。如果按照V4V3V2V1传入,则会得到一个左右翻转的纹理
(1)什么是深度?
深度其实就是该象素点在3d世界中距离摄象机的距离(绘制坐标),深度缓存中存储着每个象素点(绘制在屏幕上的)的深度值!
深度值(Z值)越大,则离摄像机越远。
深度值是存储在深度缓存里面的,我们用深度缓存的位数来衡量深度缓存的精度。深度缓存位数越高,则精确度越高,目前的显卡一般都可支持16位的Z Buffer,一些高级的显卡已经可以支持32位的Z Buffer,但一般用24位Z Buffer就已经足够了。
(2)为什么需要深度?
在不使用深度测试的时候,如果我们先绘制一个距离较近的物体,再绘制距离较远的物体,则距离远的物体因为后绘制,会把距离近的物体覆盖掉,这样的效果并不是我们所希望的。而有了深度缓冲以后,绘制物体的顺序就不那么重要了,都能按照远近(Z值)正常显示,这很关键。
实际上,只要存在深度缓冲区,无论是否启用深度测试,OpenGL在像素被绘制时都会尝试将深度数据写入到缓冲区内,除非调用了glDepthMask(GL_FALSE)来禁止写入。这些深度数据除了用于常规的测试外,还可以有一些有趣的用途,比如绘制阴影等等。
(3)启用深度测试
使用 glEnable(GL_DEPTH_TEST);
在默认情况是将需要绘制的新像素的z值与深度缓冲区中对应位置的z值进行比较,如果比深度缓存中的值小,那么用新像素的颜色值更新帧缓存中对应像素的颜色值。
但是可以使用glDepthFunc(func)来对这种默认测试方式进行修改。
其中参数func的值可以为GL_NEVER(没有处理)、GL_ALWAYS(处理所有)、GL_LESS(小于)、GL_LEQUAL(小于等于)、GL_EQUAL(等于)、GL_GEQUAL(大于等于)、GL_GREATER(大于)或GL_NOTEQUAL(不等于),其中默认值是GL_LESS。
一般来将,使用glDepthFunc(GL_LEQUAL);来表达一般物体之间的遮挡关系。
(4)启用了深度测试,那么这就不适用于同时绘制不透明物体。
在测试阶段之后,如果像素依然没有被剔除,那么像素的颜色将会和帧缓冲区中颜色附着上颜色进行混合,混合的算法可以通过OpenGL的函数进行指定,但是OpenGL提供的混合算法有限,如果需要更加复杂的混合算法,一般可以通过像素着色器进行实现,当然性能会比原生的混合算法要差一些。混合是实现物体透明度的一种技术。就是说一个物体的颜色是本身的颜色和它背后其它物体的颜色的不同强度混合
着色器是使用一种叫GLSL的类C语言写成的。GLSL是为图形计算量身定制的,它包含一些针对向量和矩阵操作的有用特性。着色器的开头总是要声明版本,接着是输入和输出变量、uniform和main函数。每个着色器的入口点都是main函数,在这个函数中我们处理所有的输入变量,并将结果输出到输出变量中。
GLSL中的数据类型主要分为标量、向量、矩阵、采样器、结构体、数组、空类型七种类型:
根据现实生活中的经历我们知道,对一个场景,随着相机的位置、拍摄出来的画面也是不相同。将相机对应于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上的分量
OpenGL 的世界是3D的,但是手机屏幕能够给我展示的终究是一个平面,只不过是在绘制的过程中利用色彩和线条让画面呈现出3D的效果。OpenGL 将这种从3D到2D的转换过程利用投影的方式使计算相对使用者来说变得简单可设置。
OpenGL 中有两种投影方式:即透视投影(perspective projection)和正交投影( orthographic projection)。
1.透视投影的投影线相交于一点,因此投影的结果与原物体的实际大小并不一致,而是会近大远小。因此透视投影更接近于真实世界的投影方式。
2.正交投影是平行投影的一种特殊情形,正交投影的投影线垂直于观察平面。平行投影的投影线相互平行,投影的结果与原物体的大小相等,因此广泛地应用于工程制图等方面。

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 ES世界的图形基础。无论多么复杂的几何物体,在OpenGL ES的世界里都可以用三角形拼成

<uses-feature android:glEsVersion="0x00020000" android:required="true" />
// 3.0的版本为0x00030000,3.1的版本为0x00030001。
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);
}
}
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) {
// 绘制
}
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物体中所有顶点都相同的量。比如光源位置,统一变换矩阵等。
}

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}; //蓝色
@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);
}


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
};
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个三角形
如图用顶点法传不同的绘制方式结果:

GL_TRIANGLE_STRIP
GL_TRIANGLE_STRIP的方式绘制连续的三角形,比直接用GL_TRIANGLES的方式绘制三角形少好多个顶点,效率会高很多。另外,GL_TRIANGLE_STRIP并不是只能绘制连续的三角形构成的物体,我们只需要将不需要重复绘制的点重复两次即可。比如,传入ABCDEEFFGH坐标,就会得到ABC、BCD、CDE以及FGH四个三角形
GL_TRIANGLE_FAN
扇面绘制是以第一个为零点进行绘制,通常我们绘制圆形,圆锥的锥面都会使用到,值得注意的是,最后一个点的左边应当与第二个点重合,在计算的时候,起点角度为0度,终点角度应包含360度。
索引法区别的地方:
// 索引内存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);

//解析二进制的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;
}
//开启光照
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));
}

我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po