OpenGL之基礎(chǔ)
OpenGL之繪制簡單形狀
OpenGL之顏色
OpenGL之調(diào)整屏幕寬高比
OpenGL之三維
OpenGL之紋理
三角形帶
要構(gòu)建圓兰迫,可以使用一個(gè)三角形扇
要構(gòu)建圓柱的側(cè)面,可以使用三角形帶(Triangle Strip)涯塔。與三角形扇一樣声登,三角形帶讓我們定義多個(gè)三角形而不用重復(fù)那些三角形中共有的點(diǎn)侣滩,如圖:
image-20211221211226581.png
三角形帶的前三個(gè)頂點(diǎn)定義了第一個(gè)三角形,之后的每個(gè)額外的頂點(diǎn)都定義了另外的一個(gè)三角形罢绽,為了使用三角形帶定義這個(gè)圓柱體的側(cè)面浴讯,只需要把這個(gè)帶繞成一個(gè)管子擎厢,并確保最后兩個(gè)頂點(diǎn)與最前面的兩個(gè)頂點(diǎn)一致
添加幾何圖形的類
public class Point {
private float mX;
private float mY;
private float mZ;
public Point(float x, float y, float z) {
this.mX = x;
this.mY = y;
this.mZ = z;
}
/**
* 沿著y軸移動(dòng)點(diǎn)
* @param y
* @return
*/
public Point translateY(float y) {
return new Point(mX, mY + y, mZ);
}
public float getX() {
return mX;
}
public float getY() {
return mY;
}
public float getZ() {
return mZ;
}
}
public class Circle {
private Point mCenter;
private float mRadius;
/**
* @param center
* @param radius
*/
public Circle(Point center, float radius) {
this.mCenter = center;
this.mRadius = radius;
}
public Point getCenter() {
return mCenter;
}
public float getRadius() {
return mRadius;
}
}
public class Cylinder {
private Circle mCircle;
private float mHeight;
public Cylinder(Circle circle, float height) {
mCircle = circle;
mHeight = height;
}
public Circle getCircle() {
return mCircle;
}
public float getHeight() {
return mHeight;
}
}
添加物體構(gòu)建器
public class ObjectBuilder {
private static final int FLOAT_PER_VERTEX = 3;
// 用于保存生成的頂點(diǎn)數(shù)據(jù)和繪制命令
protected GeneratedData mGeneratedData;
// 記錄下一個(gè)頂點(diǎn)的位置
protected int mOffset;
// 保存頂點(diǎn)的位置
protected float[] mVertexData;
public ObjectBuilder(int vertexSize) {
mVertexData = new float[vertexSize * FLOAT_PER_VERTEX];
mGeneratedData = new GeneratedData(mVertexData);
}
/**
* 創(chuàng)建冰球
* @param circle
* @param cylinder
* @param numPoints
* @return
*/
public static GeneratedData createPuck(Circle circle, Cylinder cylinder, int numPoints) {
int size = sizeOfVertexInCircle(numPoints) + sizeOfVertexInCylinder(numPoints);
ObjectBuilder builder = new ObjectBuilder(size);
builder.addCircle(circle, numPoints);
builder.addCylinder(cylinder, numPoints);
return builder.build();
}
/**
* 創(chuàng)建木槌
* @param topCircle
* @param topCylinder
* @param bottomCircle
* @param bottomCylinder
* @param numPoints
* @return
*/
public static GeneratedData createMallet(Circle topCircle, Cylinder topCylinder,
Circle bottomCircle, Cylinder bottomCylinder, int numPoints) {
int size = (sizeOfVertexInCircle(numPoints) + sizeOfVertexInCylinder(numPoints)) * 2;
ObjectBuilder builder = new ObjectBuilder(size);
builder.addCircle(topCircle, numPoints);
builder.addCylinder(topCylinder, numPoints);
builder.addCircle(bottomCircle, numPoints);
builder.addCylinder(bottomCylinder, numPoints);
return builder.build();
}
/**
* 生成頂點(diǎn)數(shù)據(jù)和繪畫指令
*
* @return
*/
public GeneratedData build() {
return mGeneratedData;
}
/**
* 添加圓柱體側(cè)面
*
* @param size
* @param cylinder
*/
protected void addCylinder(Cylinder cylinder, int size) {
int startDrawCylinderOffset = mOffset / FLOAT_PER_VERTEX;
//添加圓柱體側(cè)面的頂點(diǎn)數(shù)據(jù)
addCylinderVertex(cylinder, size);
//畫圓柱體側(cè)面命令
mGeneratedData.addDrawCommand(new GeneratedData.DrawCommand() {
@Override
public void draw() {
glDrawArrays(GL_TRIANGLE_STRIP, startDrawCylinderOffset, sizeOfVertexInCylinder(size));
}
});
}
/**
* 添加圓柱體頂端的圓
*
* @param size
* @param circle
*/
protected void addCircle(Circle circle, int size) {
int startDrawCircleOffset = mOffset / FLOAT_PER_VERTEX;
//添加圓柱體頂部圓的頂點(diǎn)數(shù)據(jù)
addCircleVertex(circle, size);
//畫圓命令
mGeneratedData.addDrawCommand(new GeneratedData.DrawCommand() {
@Override
public void draw() {
glDrawArrays(GL_TRIANGLE_FAN, startDrawCircleOffset, sizeOfVertexInCircle(size));
}
});
}
/**
* 添加圓柱體頂部圓的頂點(diǎn)數(shù)據(jù)
*
* @param circle
* @param size
*/
private void addCircleVertex(Circle circle, int size) {
Point center = circle.getCenter();
float radius = circle.getRadius();
//三角形扇的第一個(gè)點(diǎn)究流,中間的點(diǎn)
mVertexData[mOffset++] = center.getX();
mVertexData[mOffset++] = center.getY();
mVertexData[mOffset++] = center.getZ();
double angleInRadian;
for (int i = 0; i <= size; i++) {
angleInRadian = (2 * Math.PI / size) * i;
mVertexData[mOffset++] = (float) (center.getX() + radius * Math.cos(angleInRadian));
mVertexData[mOffset++] = center.getY();
mVertexData[mOffset++] = (float) (center.getZ() + radius * Math.sin(angleInRadian));
}
}
/**
* 添加圓柱體側(cè)面的頂點(diǎn)數(shù)據(jù)
*
* @param cylinder
* @param size
*/
private void addCylinderVertex(Cylinder cylinder, int size) {
Point center = cylinder.getCircle().getCenter();
float radius = cylinder.getCircle().getRadius();
float height = cylinder.getHeight();
double angleInRadian;
for (int i = 0; i <= size; i++) {
angleInRadian = (2 * Math.PI / size) * i;
//上面的頂點(diǎn)
mVertexData[mOffset++] = (float) (center.getX() + radius * Math.cos(angleInRadian));
mVertexData[mOffset++] = center.getY() + height;
mVertexData[mOffset++] = (float) (center.getZ() + radius * Math.sin(angleInRadian));
//下面的頂點(diǎn)
mVertexData[mOffset++] = (float) (center.getX() + radius * Math.cos(angleInRadian));
mVertexData[mOffset++] = center.getY();
mVertexData[mOffset++] = (float) (center.getZ() + radius * Math.sin(angleInRadian));
}
}
/**
* 圓頂點(diǎn)數(shù)據(jù)大小
*
* @param size
* @return
*/
protected static int sizeOfVertexInCircle(int size) {
return 1 + size + 1;
}
/**
* 圓柱側(cè)邊頂點(diǎn)數(shù)據(jù)大小
*
* @param size
* @return
*/
protected static int sizeOfVertexInCylinder(int size) {
return (size + 1) * 2;
}
}
public class GeneratedData {
private List<DrawCommand> mDrawCommandList = new ArrayList<>();
private float[] mVertexData;
public GeneratedData(float[] vertexData) {
mVertexData = vertexData;
}
public void addDrawCommand(DrawCommand drawCommand) {
mDrawCommandList.add(drawCommand);
}
public float[] getVertexData() {
return mVertexData;
}
public List<DrawCommand> getDrawCommandList() {
return mDrawCommandList;
}
public interface DrawCommand {
void draw();
}
}
新建冰球類
同樣的方式去更新木槌類
public class Puck {
private final GeneratedData mGeneratedData;
private final Point centerPoint = new Point(0f, 0f, 0f);
private VertexArray mVertexArray;
public Puck(float radius, float height, int size) {
// 創(chuàng)建圓柱體頂部圓
Point topCircleCenter = new Point(centerPoint.getX(), centerPoint.getY(), centerPoint.getZ());
Circle circle = new Circle(topCircleCenter, radius);
//創(chuàng)建圓柱體側(cè)面
Circle cylinderCircle = new Circle(centerPoint, radius);
Cylinder cylinder = new Cylinder(cylinderCircle, height);
mGeneratedData = ObjectBuilder.createPuck(circle, cylinder, size);
mVertexArray = new VertexArray(mGeneratedData.getVertexData());
}
/**
* 頂點(diǎn)數(shù)據(jù)綁定到OpenGL程序
*
* @param colorProgram
*/
public void bindData(ColorProgram colorProgram) {
mVertexArray.setVertexAttribPointer(
0,
colorProgram.getAPosition(),
POSITION_COMPONENT_COUNT,
0
);
}
/**
* 畫冰球
*/
public void draw() {
List<GeneratedData.DrawCommand> drawCommandList = mGeneratedData.getDrawCommandList();
for (GeneratedData.DrawCommand drawCommand : drawCommandList) {
drawCommand.draw();
}
}
}
更新著色器
頂點(diǎn)著色器
attribute vec4 a_Position;
uniform mat4 u_Matrix;
void main(){
gl_Position=u_Matrix*a_Position;
gl_PointSize=10.0;
}
片段著色器
precision mediump float;
uniform vec4 u_Color;
void main(){
gl_FragColor=u_Color;
}
更新冰球?qū)?yīng)的程序
public class ColorProgram extends BaseProgram {
private final int mAPosition;
private final int mUMatrix;
private final int mUColor;
public ColorProgram(Context context) {
super(context, R.raw.buildobjects6_color_vertex, R.raw.buildobjects6_color_fragment);
mAPosition = glGetAttribLocation(mProgram, A_POSITION);
mUColor = glGetUniformLocation(mProgram, U_COLOR);
mUMatrix = glGetUniformLocation(mProgram, U_MATRIX);
}
public void setUniforms(float[] matrix, float r, float g, float b) {
glUniformMatrix4fv(mUMatrix, 1, false, matrix, 0);
glUniform4f(mUColor, r, g, b, 1f);
}
public int getAPosition() {
return mAPosition;
}
}
矩陣總結(jié)
矩陣 | 說明 |
---|---|
模型矩陣 | 移動(dòng)辣吃、旋轉(zhuǎn)、平移單獨(dú)的某個(gè)物體 |
視圖矩陣 | 平等地影響場景中的每一·個(gè)物體芬探,相當(dāng)于移動(dòng)相機(jī) |
投影矩陣 | 幫助創(chuàng)建三維的幻象神得,通常只有當(dāng)屏幕變換方位時(shí),它才會(huì)變化 |
視圖矩陣
使用 Matrix.setLookAtM 方法創(chuàng)建視圖矩陣
方法原型
public static void setLookAtM(float[] rm, int rmOffset,
float eyeX, float eyeY, float eyeZ,
float centerX, float centerY, float centerZ, float upX, float upY,
float upZ) {
參數(shù)說明
參數(shù) | 說明 |
---|---|
float[] rm | 最終生成的視圖矩陣數(shù)組偷仿,長度至少容納16個(gè)元素哩簿,以便它能存儲(chǔ)視圖 |
int rmOffset | 從 rmOffset 的這個(gè)偏移值開始存進(jìn)rm |
float eyeX, float eyeY, float eyeZ, | 這是眼睛所在的位置,場景中的所有東西看起來都像是從這個(gè)點(diǎn)觀察它們一樣 |
float centerX, float centerY, float centerZ | 這是眼睛正在看的地方酝静,這個(gè)位置出現(xiàn)在整個(gè)場景的中心 |
float upX, float upY, float upZ | 你的頭指向的地方节榜,upY的值為1意味著你的頭筆直指向Y軸上方 |
坐標(biāo)變換總結(jié)
vertex(clip) = ProjectionMatrix * ViewMatrix * ModelMatrix * vertex(model)
修改渲染器
public class MyRenderer implements GLSurfaceView.Renderer {
private Context mContext;
// 保存模型矩陣
private float[] modelMatrix = new float[16];
// 視圖矩陣
private float[] viewMatrix = new float[16];
// 保存投影矩陣
private float[] projectionMatrix = new float[16];
// 保存視圖矩陣和投影矩陣的乘積
private float[] viewProjectionMatrix = new float[16];
// 保存模型矩陣、視圖矩陣和投影矩陣的乘積
private float[] modelViewProjectionMatrix = new float[16];
private TextureProgram mTextureProgram;
private ColorProgram mColorProgram;
private Table mTable;
private Mallet mMallet;
private int mTextureId;
private Puck mPuck;
public MyRenderer(Context context) {
mContext = context;
}
@Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
glClearColor(0f, 0f, 0f, 0f);
mTextureProgram = new TextureProgram(mContext);
mColorProgram = new ColorProgram(mContext);
// 創(chuàng)建物體
mTable = new Table();
mMallet = new Mallet(0.08f, 0.05f, 0.04f, 0.10f, 64);
mPuck = new Puck(0.06f, 0.05f, 64);
mTextureId = TextureHelper.loadTexture(mContext, R.drawable.air_hockey_surface);
}
@Override
public void onSurfaceChanged(GL10 gl10, int width, int height) {
glViewport(0, 0, width, height);
MatrixHelper.perspectiveM(projectionMatrix, 45, (float) width / height, 1, 10);
// 創(chuàng)建視圖矩陣
Matrix.setLookAtM(viewMatrix, 0, 0, 1.2f, 2.2f, 0, 0, 0, 0, 1, 0);
Matrix.multiplyMM(viewProjectionMatrix, 0, projectionMatrix, 0, viewMatrix, 0);
}
@Override
public void onDrawFrame(GL10 gl10) {
glClear(GL_COLOR_BUFFER_BIT);
// 繪制桌子
positionTableOnScreen();
mTextureProgram.useProgram();
mTextureProgram.setUniforms(modelViewProjectionMatrix, mTextureId);
mTable.bindData(mTextureProgram);
mTable.draw();
// 繪制木槌
positionObjectOnScreen(0f, 0f, 0.4f);
mColorProgram.useProgram();
mColorProgram.setUniforms(modelViewProjectionMatrix, 0f, 1f, 0f);
mMallet.bindData(mColorProgram);
mMallet.draw();
// 繪制冰球
positionObjectOnScreen(0f, 0f, 0f);
mColorProgram.useProgram();
mColorProgram.setUniforms(modelViewProjectionMatrix, 1f, 0f, 0f);
mPuck.bindData(mColorProgram);
mPuck.draw();
}
/**
* 桌子原來是以x和y坐標(biāo)定義的别智,因此要使它平放在地上宗苍,我們需要讓它繞x軸向后旋轉(zhuǎn)90度
* 不需要把桌子平移一定的距離,因?yàn)槲覀兿胍雷釉谑澜缱鴺?biāo)里保持在位置(0,0,0)
* 并且視圖矩陣已經(jīng)想辦法使桌子對我們可見了
*/
public void positionTableOnScreen() {
Matrix.setIdentityM(modelMatrix, 0);
Matrix.rotateM(modelMatrix, 0, -90, 1, 0, 0);
Matrix.multiplyMM(modelViewProjectionMatrix, 0, viewProjectionMatrix, 0, modelMatrix, 0);
}
/**
* 將物體移動(dòng)到(x,y,z)的位置
*
* @param x
* @param y
* @param z
*/
public void positionObjectOnScreen(float x, float y, float z) {
Matrix.setIdentityM(modelMatrix, 0);
Matrix.translateM(modelMatrix, 0, x, y, z);
Matrix.multiplyMM(modelViewProjectionMatrix, 0, viewProjectionMatrix, 0, modelMatrix, 0);
}
}
效果
image-20211221222748898.png