效果展示:

本篇博文总共会实现两种混色旋转的3D圆锥:

一.圆锥解析

1.1 对圆锥的拆解

上一篇博文讲解了绘制圆柱体,这一篇讲解绘制一个彩色旋转的圆锥

在绘制圆柱体时提到过,关键点是先将圆柱进行拆解,便于创建出顶点坐标数组

同样,绘制圆锥也先进行拆解

圆锥的拆解很简单,有两种方式可以理解:

2D圆的圆心从圆平面里抽离出来,赋予一个Z值2D的圆心和圆平面分别赋予不同的Z值

也就是把圆锥拆成:一个2D圆 + 扇形锥面

1.2 单位图元:三角形

讲到这里顺带提一句:

在OpenGL的世界里,不论多么复杂图形,最终都会被拆解成使用最基础的单位图元:三角形来完成绘制

为什么OpenGL渲染的基础单位图元是三角形呢?

因为一个点只是点,两个点组成线,三个点能确定一个面。

三角形是形成一个面最基础的图形单元,所以也是OpenGL的基础图元。

二.GLRender:变量定义

这次顶点颜色数组的定义和赋值与立方体绘制类似,在Render类中使用代码动态完成

2.1 常规变量定义

//MVP矩阵

private float[] mMVPMatrix = new float[16];

//着色器程序/渲染器

private int shaderProgram;

//mvp变换矩阵属性

private int mvpMatrixLoc;

//位置属性

private int aPositionLocation;

//颜色属性

private int aColorLocation;

//surface宽高比率

private float ratio;

2.2 定义顶点坐标数组和缓冲

//圆锥锥顶 顶点

private float vertexData[];

//圆锥底部圆 顶点

private float vertexData1[];

//圆锥锥顶 顶点颜色

private float colorData[];

//圆锥底部圆 顶点颜色

private float colorData1[];

//对应的坐标和颜色缓冲

private FloatBuffer vertexBuffer;

private FloatBuffer vertexBuffer1;

private FloatBuffer colorBuffer;

private FloatBuffer colorBuffer1;

2.3 定义MVP矩阵

//MVP矩阵

private float[] mMVPMatrix = new float[16];

三.GLRender:着色器、内存分配等

3.1 着色器创建、链接、使用

3.2 着色器属性获取、赋值

3.3 缓冲内存分配

这几个部分的代码实现2D图形绘制基本一致

可参考以前2D绘制的相关博文,里面都有详细的代码实现

不再重复展示代码

四.GLRender:动态创建顶点

需要传入两个参数:

圆锥底部圆半径长度底部圆和圆锥扇面分割份数

createPositions(0.6f, 60);

函数实现:

private void createPositions(float radius, int n) {

ArrayList red = new ArrayList<>();

ArrayList blue = new ArrayList<>();

ArrayList magenta = new ArrayList<>();

ArrayList totalColor1 = new ArrayList<>();

ArrayList totalColor2 = new ArrayList<>();

//红

red.add(1.0f);

red.add(0.0f);

red.add(0.0f);

red.add(0.0f);

//蓝

blue.add(0.0f);

blue.add(0.0f);

blue.add(1.0f);

blue.add(0.0f);

//粉 Magenta

magenta.add(1.0f);

magenta.add(0.2f);

magenta.add(1.0f);

magenta.add(0.0f);

ArrayList data = new ArrayList<>();

//设置圆心的顶点坐标

data.add(0.0f);

data.add(0.0f);

data.add(1.0f);

//设置底部圆的顶点坐标

float angDegSpan = 360f / n;

for (float i = 0; i < 360 + angDegSpan; i += angDegSpan) {

data.add((float) (radius * Math.sin(i * Math.PI / 180f)));

data.add((float) (radius * Math.cos(i * Math.PI / 180f)));

//底部圆的顶点Z坐标设置为-0.5f

data.add(-0.5f);

}

//所有顶点坐标

float[] f = new float[data.size()];

for (int i = 0; i < f.length; i++) {

f[i] = data.get(i);

}

vertexData = f;

//设置圆心和底部圆顶点对应的颜色数据

colorData = new float[f.length * 4 / 3];

for (int i = 0; i < f.length / 3; i++) {

if (i == 0) {

totalColor1.addAll(red);

} else {

totalColor1.addAll(blue);

}

}

for (int i = 0; i < totalColor1.size(); i++) {

colorData[i] = totalColor1.get(i);

}

//底部圆

vertexData1 = new float[vertexData.length];

for (int i = 0; i < vertexData.length; i++) {

if (i == 2) {

vertexData1[i] = -0.5f;

} else {

vertexData1[i] = vertexData[i];

}

}

colorData1 = new float[f.length * 4 / 3];

for (int i = 0; i < f.length / 3; i++) {

totalColor2.addAll(magenta);

}

for (int i = 0; i < totalColor2.size(); i++) {

colorData1[i] = totalColor2.get(i);

}

}

五.GLRender:绘制

5.1 MVP矩阵

//MVP矩阵赋值

mMVPMatrix = TransformUtils.getConeMVPMatrix(ratio);

//将变换矩阵传入顶点渲染器

glUniformMatrix4fv(mvpMatrixLoc, 1, false, mMVPMatrix, 0);

getConeMVPMatrix():

采用的是透视投影方式

public static float[] getConeMVPMatrix(float ratio) {

float[] modelMatrix = getIdentityMatrix(16, 0); //模型变换矩阵

float[] viewMatrix = getIdentityMatrix(16, 0); //观测变换矩阵/相机矩阵

float[] projectionMatrix = getIdentityMatrix(16, 0); //投影变换矩阵

mConeRotateAgree = (mConeRotateAgree + 1) % 360;

//旋转方向xyz三个轴是相对于相机观察方向的,可以写一篇博客

Matrix.rotateM(modelMatrix, 0, mConeRotateAgree, 1, 0, 1); //获取模型旋转变换矩阵

//设置相机位置

Matrix.setLookAtM(viewMatrix, 0, 5, 0.0f, -3.0f, 0f, 0f, 0f, 0f, 0.0f, 1.0f);

//设置透视投影

Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1, 1, 3, 10);

//计算变换矩阵

float[] tmpMatrix = new float[16];

Matrix.multiplyMM(tmpMatrix, 0, viewMatrix, 0, modelMatrix, 0);

float[] mvpMatrix = new float[16];

Matrix.multiplyMM(mvpMatrix, 0, projectionMatrix, 0, tmpMatrix, 0);

return mvpMatrix;

}

5.2 绘制圆锥的锥顶锥面、底部圆

drawCenterAndSide();

drawBottomCircle();

(1).drawCenterAndSide()

//准备顶点坐标和颜色数据

glVertexAttribPointer(aPositionLocation, 3, GL_FLOAT, false, 0, vertexBuffer);

glVertexAttribPointer(aColorLocation, 4, GL_FLOAT, false, 0, colorBuffer);

//绘制

glDrawArrays(GL_TRIANGLE_FAN, 0, vertexData.length / 3);

(2).drawBottomCircle()

//准备顶点坐标和颜色数据

glVertexAttribPointer(aPositionLocation, 3, GL_FLOAT, false, 0, vertexBuffer1);

//底部圆颜色(粉色)缓冲

glVertexAttribPointer(aColorLocation, 4, GL_FLOAT, false, 0, colorBuffer1);

//绘制

glDrawArrays(GL_TRIANGLE_FAN, 0, vertexData1.length / 3);

六.着色器代码

(1).cone_vertex_shader.glsl

#version 300 es

layout (location = 0) in vec4 vPosition;

layout (location = 1) in vec4 aColor;

uniform mat4 u_Matrix;

out vec4 vColor;

void main() {

gl_Position = u_Matrix*vPosition;

vColor = aColor;

}

(2).cone_fragtment_shader.glsl

#version 300 es

#extension GL_OES_EGL_image_external_essl3 : require

precision mediump float;

in vec4 vColor;

out vec4 outColor;

void main(){

outColor = vColor;

}

七.两种效果

最终实现出来的是锥面红蓝渐变、锥底粉色的圆锥

个人这个效果并不太好,底部和锥面的颜色变化没有渐变,过于突兀 

只要在绘制底部圆的函数中更改一下,就可以得到底部圆心对应锥顶颜色,圆周对应锥面底部颜色的圆锥

注释掉底部圆的颜色缓冲代码

//准备顶点坐标和颜色数据

glVertexAttribPointer(aPositionLocation, 3, GL_FLOAT, false, 0, vertexBuffer1);

//注释掉这句,底部圆的圆心颜色就会和圆锥锥顶颜色一样,底部圆的圆周颜色和圆锥锥面底部颜色一样

//glVertexAttribPointer(aColorLocation, 4, GL_FLOAT, false, 0, colorBuffer1);

//绘制

glDrawArrays(GL_TRIANGLE_FAN, 0, vertexData1.length / 3);

效果如下:

八.结束语

两种混色旋转的3D圆锥绘制过程到此讲解结束

下一篇博文讲解混色旋转的3D球体绘制

相关阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: