一 OpenGL中多边形
OpenGL中多边形都是由一个个三角形图元构成,三角形具有稳定性,三角形是OpenGLES提供的最复杂的图元单位。所以此处先以三角形的绘制作为OpenGL ES的入门。
二、三角形绘制
OpenGL图形的绘制流程基本差不多,先要定义顶点着色器和片元着色器。顶点着色器和片元着色器使用glsl语言编写。
顶点着色器:
#version 300 es
layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec4 aColor;
uniform mat4 vMatrix;
out vec4 vColor;
void main() {
gl_Position = vMatrix * vPosition;
gl_PointSize = 10.0;
vColor = aColor;
}
mat4是4阶浮点方阵。vec4是4维浮点向量。in 代表输入,out代表输出。
uniform变量:uniform变量是外部application程序传递给(vertex和fragment)shader的变量。
attribute变量:只能在vertex shader中使用的变量。
varying变量:varying变量是vertex和fragment shader之间做数据传递用的。一般vertex shader修改varying变量的值,然后fragment shader使用该varying变量的值。因此varying变量在vertex和fragment shader二者之间的声明必须是一致的。
片元着色器:
#version 300 es
precision mediump float;
in vec4 vColor;
out vec4 fragColor;
void main() {
fragColor = vColor;
}
渲染器器代码如下:
/** 等腰直角三角形 + RGB 三个顶点红绿蓝三种颜色渲染。
*
* **/
public class TriangleRenderer implements GLSurfaceView.Renderer{
private FloatBuffer vertexBuffer;
private FloatBuffer colorBuffer;
//渲染程序
private int mProgram;
//3个定点,等腰直角
static float triangleCoords[] ={
0.5f, 0.5f, 0.0f, // top
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f // bottom right
};
private float color[] = {
0.0f, 1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f
};
//相机矩阵
private final float[] mViewMatrix = new float[16];
//投影矩阵
private final float[] mProjectMatrix = new float[16];
//最终变换矩阵
private final float[] mMVPMatrix = new float[16];
// //变换矩阵
// private int uMatrixLocation;
public TriangleRenderer() {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(triangleCoords.length*4);
byteBuffer.order(ByteOrder.nativeOrder());
vertexBuffer = byteBuffer.asFloatBuffer();
//把这门语法() 推送给GPU
vertexBuffer.put(triangleCoords);
vertexBuffer.position(0);
colorBuffer = ByteBuffer.allocateDirect(color.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
//传入指定的数据
colorBuffer.put(color);
colorBuffer.position(0);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES30.glClearColor(0.5f,0.5f,0.5f,1.0f);
//编译顶点着色程序
String vertexShaderStr = ResReadUtils.readResource(R.raw.vertex_base_matrix_shader);
int vertexShaderId = ShaderUtils.compileVertexShader(vertexShaderStr);
//编译片段着色程序
String fragmentShaderStr = ResReadUtils.readResource(R.raw.fragment_base_common_shader);
int fragmentShaderId = ShaderUtils.compileFragmentShader(fragmentShaderStr);
//连接程序
mProgram = ShaderUtils.linkProgram(vertexShaderId, fragmentShaderId);
//在OpenGLES环境中使用程序
GLES30.glUseProgram(mProgram);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES30.glViewport(0, 0, width, height);
float ratio = (float) width/height;
//设置透视投影
Matrix.frustumM(mProjectMatrix,0,-ratio,ratio,-1,1,3,7);
//设置相机位置
Matrix.setLookAtM(mViewMatrix,0,0,0,5.0f,//摄像机坐标
0f,0f,0f,//目标物的中心坐标
0f,1.0f,0.0f);//相机方向
//接着是摄像机顶部的方向了,如下图,很显然相机旋转,up的方向就会改变,这样就会会影响到绘制图像的角度。
//例如设置up方向为y轴正方向,upx = 0,upy = 1,upz = 0。这是相机正对着目标图像
//计算变换矩阵
Matrix.multiplyMM(mMVPMatrix,0,mProjectMatrix,0,mViewMatrix,0);
}
@Override
public void onDrawFrame(GL10 gl) {
//左乘矩阵
int uMaxtrixLocation = GLES30.glGetUniformLocation(mProgram,"vMatrix");
// 将前面计算得到的mMVPMatrix(frustumM setLookAtM 通过multiplyMM 相乘得到的矩阵) 传入vMatrix中,与顶点矩阵进行相乘
GLES30.glUniformMatrix4fv(uMaxtrixLocation,1,false,mMVPMatrix,0);
int aPositionLocation = GLES30.glGetAttribLocation(mProgram,"vPosition");
GLES30.glEnableVertexAttribArray(aPositionLocation);
//x y z 所以数据size 是3
GLES30.glVertexAttribPointer(aPositionLocation,3,GLES30.GL_FLOAT,false,0,vertexBuffer);
int aColorLocation = GLES20.glGetAttribLocation(mProgram,"aColor");
//准备颜色数据 rgba 所以数据size是 4
GLES30.glVertexAttribPointer(aColorLocation, 4, GLES30.GL_FLOAT, false, 0, colorBuffer);
//启用顶点颜色句柄
GLES30.glEnableVertexAttribArray(aColorLocation);
GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 3);
//禁止顶点数组的句柄
GLES30.glDisableVertexAttribArray(aPositionLocation);
GLES30.glDisableVertexAttribArray(aColorLocation);
}
}
将顶点数据传递给vPosition变量,将投影、视图矩阵相乘的结果赋值给vMatrix,然后与顶点坐标矩阵左乘,从而作用到顶点坐标上。
绘制效果如下:
三、三角形纹理贴图
纹理映射时只需要为物体的顶点指定纹理坐标即可,其余部分由片元着色器插值完成。所以核心工作就是将三角形的每个顶点坐标,找到对应的纹理坐标即可。
顶点着色器:
#version 300 es
layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec2 aTextureCoord;
uniform mat4 vMatrix;
out vec2 vTexCoord;
void main() {
gl_Position = vMatrix * vPosition;
gl_PointSize = 10.0;
vTexCoord = aTextureCoord;
}
片元着色器:
vertex_triangle_texture_shader.glsl
#version 300 es
precision mediump float;
uniform sampler2D uTextureUnit;
in vec2 vTexCoord;
out vec4 vFragColor;
void main() {
vFragColor = texture(uTextureUnit,vTexCoord);
}
渲染器代码:
/** 等腰直角三角形+纹理贴图
*
* **/
public class TriangleTextureRenderer implements GLSurfaceView.Renderer{
private static final String TAG = "TriangleTextureRenderer";
private FloatBuffer vertexBuffer;
private FloatBuffer textureBuffer;
//渲染程序
private int mProgram;
//3个定点,等腰直角
static float triangleCoords[] ={
0.5f, 0.5f, 0.0f, // top
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f // bottom right
};
//纹理坐标1
// private float textureVertex[] = {
// 0.75f, 0.25f,
// 0.25f, 0.75f,
// 0.75f, 0.75f,
// };
//纹理坐标2
// 三角形3个定点对应在纹理坐标系中的坐标
private float textureVertex[] = {
1.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
};
//相机矩阵
private final float[] mViewMatrix = new float[16];
//投影矩阵
private final float[] mProjectMatrix = new float[16];
//最终变换矩阵
private final float[] mMVPMatrix = new float[16];
//纹理id
private int textureId;
// //变换矩阵
// private int uMatrixLocation;
public TriangleTextureRenderer() {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(triangleCoords.length*4);
byteBuffer.order(ByteOrder.nativeOrder());
vertexBuffer = byteBuffer.asFloatBuffer();
//把这门语法() 推送给GPU
vertexBuffer.put(triangleCoords);
vertexBuffer.position(0);
textureBuffer = ByteBuffer.allocateDirect(textureVertex.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
//传入指定的数据
textureBuffer.put(textureVertex);
textureBuffer.position(0);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES30.glClearColor(0.5f,0.5f,0.5f,1.0f);
//编译顶点着色程序
String vertexShaderStr = ResReadUtils.readResource(R.raw.vertex_triangle_texture_shader);
int vertexShaderId = ShaderUtils.compileVertexShader(vertexShaderStr);
//编译片段着色程序
String fragmentShaderStr = ResReadUtils.readResource(R.raw.fragment_triangle_texture_shader);
int fragmentShaderId = ShaderUtils.compileFragmentShader(fragmentShaderStr);
//连接程序
mProgram = ShaderUtils.linkProgram(vertexShaderId, fragmentShaderId);
//在OpenGLES环境中使用程序
GLES30.glUseProgram(mProgram);
//加载纹理
textureId = TextureUtils.loadTexture(AppCore.getInstance().getContext(),R.drawable.world_map);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES30.glViewport(0, 0, width, height);
float ratio = (float) width/height;
//设置透视投影
Matrix.frustumM(mProjectMatrix,0,-ratio,ratio,-1,1,3,7);
//设置相机位置
Matrix.setLookAtM(mViewMatrix,0,0,0,3.6f,//摄像机坐标
0f,0f,0f,//目标物的中心坐标
0f,1.0f,0.0f);//相机方向
//接着是摄像机顶部的方向了,如下图,很显然相机旋转,up的方向就会改变,这样就会会影响到绘制图像的角度。
//例如设置up方向为y轴正方向,upx = 0,upy = 1,upz = 0。这是相机正对着目标图像
//计算变换矩阵
Matrix.multiplyMM(mMVPMatrix,0,mProjectMatrix,0,mViewMatrix,0);
}
@Override
public void onDrawFrame(GL10 gl) {
//左乘矩阵
int uMaxtrixLocation = GLES30.glGetUniformLocation(mProgram,"vMatrix");
// 将前面计算得到的mMVPMatrix(frustumM setLookAtM 通过multiplyMM 相乘得到的矩阵) 传入vMatrix中,与顶点矩阵进行相乘
GLES30.glUniformMatrix4fv(uMaxtrixLocation,1,false,mMVPMatrix,0);
int aPositionLocation = GLES30.glGetAttribLocation(mProgram,"vPosition");
GLES30.glEnableVertexAttribArray(aPositionLocation);
//x y z 所以数据size 是3
GLES30.glVertexAttribPointer(aPositionLocation,3,GLES30.GL_FLOAT,false,0,vertexBuffer);
int aTextureLocation = GLES20.glGetAttribLocation(mProgram,"aTextureCoord");
Log.e(TAG, "onDrawFrame: textureLocation="+aTextureLocation);
//纹理坐标数据 x、y,所以数据size是 2
GLES30.glVertexAttribPointer(aTextureLocation, 2, GLES30.GL_FLOAT, false, 0, textureBuffer);
//启用顶点颜色句柄
GLES30.glEnableVertexAttribArray(aTextureLocation);
GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
//绑定纹理
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D,textureId);
GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 3);
//禁止顶点数组的句柄
GLES30.glDisableVertexAttribArray(aPositionLocation);
GLES30.glDisableVertexAttribArray(aTextureLocation);
}
}
绘制效果如下:
完整贴图如下:
使用纹理坐标2:
使用纹理坐标1:
可以看到,对于同一个纹理,使用不同纹理坐标进行贴图时,实际就能显示贴图的不同部位。
代码:
https://github.com/godtrace12/DOpenglTest.git
主要参考:
https://blog.csdn.net/byhook/article/details/83747146
https://blog.csdn.net/gongxiaoou/article/details/89463784
https://blog.csdn.net/byhook/article/details/83990792
https://blog.csdn.net/jklwan/article/details/104010383
https://blog.csdn.net/z444_579/article/details/51967037