去年有分析了一些Opengl ES的实例,但是后面在实际的工作中,发现根基不牢,工作中使用的一些复杂场景还是理解的不够透彻,所以回过心来,必须把基础把扎实。从这节开始,我们后面对一些非常基础普通的Opengl ES API进行实际使用介绍,万丈高楼平地起,必须把基础搞扎实,我们才能一步步向上。
所有实例均有提供源码,下载地址:Opengl ES Source Code。
网上找到一份非常好的资料,收集了很多API的英文翻译,供大家参考:OpenGL ES 2.0 中文API。
好,本节我们来看一下glViewport API的作用。实现的实例如下图:
很简单,我们就是不断的变化视口,然后每个视口画一个三角形。本节的源码是在OpenGL\learn\src\main\java\com\opengl\learn\GlViewportRender.java文件中,该类所有源码如下:
package com.opengl.learn;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.util.Log;
import com.lime.common.ESShader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;
import static android.opengl.GLES20.GL_COMPILE_STATUS;
import static android.opengl.GLES20.GL_FLOAT;
import static android.opengl.GLES20.GL_FRAGMENT_SHADER;
import static android.opengl.GLES20.GL_LINK_STATUS;
import static android.opengl.GLES20.GL_TRIANGLES;
import static android.opengl.GLES20.GL_VERTEX_SHADER;
import static android.opengl.GLES20.glAttachShader;
import static android.opengl.GLES20.glClear;
import static android.opengl.GLES20.glClearColor;
import static android.opengl.GLES20.glCompileShader;
import static android.opengl.GLES20.glCreateProgram;
import static android.opengl.GLES20.glCreateShader;
import static android.opengl.GLES20.glDeleteProgram;
import static android.opengl.GLES20.glDeleteShader;
import static android.opengl.GLES20.glDrawArrays;
import static android.opengl.GLES20.glEnableVertexAttribArray;
import static android.opengl.GLES20.glGetAttribLocation;
import static android.opengl.GLES20.glGetError;
import static android.opengl.GLES20.glGetProgramInfoLog;
import static android.opengl.GLES20.glGetProgramiv;
import static android.opengl.GLES20.glGetShaderInfoLog;
import static android.opengl.GLES20.glGetShaderiv;
import static android.opengl.GLES20.glLinkProgram;
import static android.opengl.GLES20.glShaderSource;
import static android.opengl.GLES20.glUseProgram;
import static android.opengl.GLES20.glVertexAttribPointer;
import static android.opengl.GLES20.glViewport;
public class GlViewportRender implements GLSurfaceView.Renderer {
private static String TAG = GlViewportRender.class.getSimpleName();
private final float[] mVerticesData =
{0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f};
private final float[] mColorData =
{
1.0f, 0.0f, 0.0f, 1.0f, // c0
0.0f, 1.0f, 0.0f, 1.0f, // c1
0.0f, 0.0f, 1.0f, 1.0f // c2
};
private static final int BYTES_PER_FLOAT = 4;
private static final int POSITION_COMPONENT_SIZE = 3;
private static final int COLOR_COMPONENT_SIZE = 4;
private Context mContext;
private int mProgramObject;
private int mWidth;
private int mHeight;
private FloatBuffer mVertices, mColorVertices;
private int vPosition, vColor;
private static final int STRIDE =
(POSITION_COMPONENT_SIZE + COLOR_COMPONENT_SIZE) * BYTES_PER_FLOAT;
public GlViewportRender(Context context) {
mContext = context;
mVertices = ByteBuffer.allocateDirect(mVerticesData.length * 4)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mVertices.put(mVerticesData).position(0);
mColorVertices = ByteBuffer.allocateDirect(mColorData.length * 4)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mColorVertices.put(mColorData).position(0);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
loadProgram();
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
mWidth = width;
mHeight = height;
}
@Override
public void onDrawFrame(GL10 gl) {
drawShape();
}
private void drawShape() {
// Clear the color buffer
glClear(GL_COLOR_BUFFER_BIT);
for (int i = 0; i < 4; i++) {
if (i == 0) {
glViewport(0, mHeight / 2, mWidth / 2, mHeight / 2);
} else if (i == 1) {
glViewport(mWidth / 2, mHeight / 2, mWidth / 2, mHeight / 2);
} else if (i == 2) {
glViewport(0, 0, mWidth / 2, mHeight / 2);
} else if (i == 3) {
glViewport(mWidth / 2, 0, mWidth / 2, mHeight / 2);
}
// Use the program object
glUseProgram(mProgramObject);
mVertices.position(0);
// Load the vertex data
glVertexAttribPointer(vPosition, 3, GL_FLOAT, false, STRIDE, mVertices);
glEnableVertexAttribArray(vPosition);
mVertices.position(POSITION_COMPONENT_SIZE);
glVertexAttribPointer(vColor, 4, GL_FLOAT, false, STRIDE, mVertices);
glEnableVertexAttribArray(vColor);
glDrawArrays(GL_TRIANGLES, 0, 3);
}
}
private void loadProgram() {
String vShaderStr = ESShader.readShader(mContext, "viewport_vertexShader.glsl");
String fShaderStr = ESShader.readShader(mContext, "viewport_fragmentShader.glsl");
int vertexShader;
int fragmentShader;
int programObject;
int[] linked = new int[1];
// Load the vertex/fragment shaders
vertexShader = loadShader(GL_VERTEX_SHADER, vShaderStr);
fragmentShader = loadShader(GL_FRAGMENT_SHADER, fShaderStr);
// Create the program object
programObject = glCreateProgram();
if (programObject == 0) {
return;
}
glAttachShader(programObject, vertexShader);
glAttachShader(programObject, fragmentShader);
// Link the program
glLinkProgram(programObject);
// Check the link status
glGetProgramiv(programObject, GL_LINK_STATUS, linked, 0);
if (linked[0] == 0) {
Log.e(TAG, "Error linking program:");
Log.e(TAG, glGetProgramInfoLog(programObject));
glDeleteProgram(programObject);
return;
}
// Store the program object
mProgramObject = programObject;
glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
vPosition = glGetAttribLocation(mProgramObject, "vPosition");
vColor = glGetAttribLocation(mProgramObject, "vColor");
Log.i(TAG, "vPosition: " + vPosition + ", vColor: " + vColor);
}
private int loadShader(int type, String shaderSrc) {
int shader;
int[] compiled = new int[1];
// Create the shader object
shader = glCreateShader(type);
if (shader == 0) {
return 0;
}
// Load the shader source
glShaderSource(shader, shaderSrc);
// Compile the shader
glCompileShader(shader);
// Check the compile status
glGetShaderiv(shader, GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
Log.e(TAG, "compile shader error: " + glGetShaderInfoLog(shader));
glGetError();
glDeleteShader(shader);
return 0;
}
Log.i(TAG, "load " + type + " shader result: " + shader);
return shader;
}
}
我们主要是演练一下glViewport API的功能,其他逻辑就简单略过了。glViewport接口的介绍请点击:GLES2.0中文API-glViewport。可以看到该接口一共有四个参数,前两个参数x、y定位了当前视口的左下角的点,以像素为单位,后面两个参数width、height定义了当前视口的大小,程序员思维应该很容易理解,一个接口就确定好了我们视口的位置和大小。那想像一下,我们平时绘制时,直接调用glViewport(0, 0, mWidth, mHeight),表示视口左下角和屏幕左下角重合,视口大小就是屏幕大小,所以视口的中心点也就是屏幕的中心点,绘制时就是按照Opengl坐标,把整个屏幕投影在(-1,1)范围内,X/Y方向都是。
明白了最基础的使用,我们再考虑一下,那如果我要添加一个水印,要指定水印的位置,要怎么办呢?对了,我们就可以修改视口的参数来达到这样的目的了,我们修改前两个参数,就可以变换视口的左下角;修改后两个参数就可以控制视口的大小。
那要实现本例的效果,想必大家感觉也很简单了,四个三角形大小相同,且宽高归一化后都是0.5f,我们要画第一个左上角的三角形,它的左下角就是屏幕左侧的中间点的坐标(0,mHeight / 2);第二个右上角的三角形,它的左下角的坐标就是屏幕的中心点(mWidth / 2, mHeight / 2);第三个左下角的三角形,它的左下角坐标也就是屏幕的左下角(0,0);第四个三角形的左下角坐标就是屏幕最底线的中间点(mWidth / 2, 0)。最后两个参数宽高都一样,这样我们就画出来四个不同位置的三角形了。
这里顺便说一下glClear(GL_COLOR_BUFFER_BIT)清屏的调用,不能在for循环中调用,否则就会出问题。比如我们把drawShape方法修改成如下:
private void drawShape() {
// Clear the color buffer
for (int i = 0; i < 4; i++) {
glClear(GL_COLOR_BUFFER_BIT);
if (i == 0) {
glViewport(0, mHeight / 2, mWidth / 2, mHeight / 2);
} else if (i == 1) {
glViewport(mWidth / 2, mHeight / 2, mWidth / 2, mHeight / 2);
} else if (i == 2) {
glViewport(0, 0, mWidth / 2, mHeight / 2);
} else if (i == 3) {
glViewport(mWidth / 2, 0, mWidth / 2, mHeight / 2);
}
// Use the program object
glUseProgram(mProgramObject);
mVertices.position(0);
// Load the vertex data
glVertexAttribPointer(vPosition, 3, GL_FLOAT, false, STRIDE, mVertices);
glEnableVertexAttribArray(vPosition);
mVertices.position(POSITION_COMPONENT_SIZE);
glVertexAttribPointer(vColor, 4, GL_FLOAT, false, STRIDE, mVertices);
glEnableVertexAttribArray(vColor);
glDrawArrays(GL_TRIANGLES, 0, 3);
}
}
我们把glClear(GL_COLOR_BUFFER_BIT)逻辑放在for循环里面了,那么想像一下,前三个三角形画完,都会执行glClear被清除,只有第四个三角形画完后退出for循环,所以也就只会显示最后一个三角形了。
还有一个需要注意的,我们该实例中顶点的位置和颜色属性都是从mVertices中取值的,所以在给颜色赋值时,必须调用mVertices.position(POSITION_COMPONENT_SIZE)移动Buffer的起始位置,否则颜色取值就会出错,出来的结果跟我们意想的就会不一样,大家可以试一下。
好了,glViewport API我们就介绍到这里吧,希望大家也能扎实的打好基础,否则实际工作中碰到的那复杂场景我们真的是束手无策。