画球
球坐标公式
球坐标可以用如下公式表示:
x=R∗cosα∗cosβ
y=R∗sinα∗cosβ
z=R∗sinβ
在Eclipse中新建工程DrawBall, 新建Ball类, 添加成员方法初始化顶点数组,获取球坐标代码如下:
/**
* 初始化顶点数组
* ANGLE_SPAN角度进行划分
* p3 p2 (beta + ANGLE_SPAN)
* p0 p1 (alpha + ANGLE_SPAN)
* 画两个三角形 p0p1p2, p0p2p3
*/
private void initVertex() {
vertex = new ArrayList<Float>();
for (int alpha = 0; alpha <= 360; alpha += ANGLE_SPAN) {
for (int beta = -90; beta <= 90; beta += ANGLE_SPAN) {
// 第一个点的坐标
float x0 = (float) (RADIAN * Math.cos(Math.toRadians(alpha)) * Math.cos(Math.toRadians(beta)));
float y0 = (float) (RADIAN * Math.sin(Math.toRadians(alpha)) * Math.cos(Math.toRadians(beta)));
float z0 = (float) (RADIAN * Math.sin(Math.toRadians(beta)));
// 第二个点的坐标
float x1 = (float) (RADIAN * Math.cos(Math.toRadians(alpha + ANGLE_SPAN)) * Math.cos(Math.toRadians(beta)));
float y1 = (float) (RADIAN * Math.sin(Math.toRadians(alpha + ANGLE_SPAN)) * Math.cos(Math.toRadians(beta)));
float z1 = (float) (RADIAN * Math.sin(Math.toRadians(beta)));
// 第三个点的坐标
float x2 = (float) (RADIAN * Math.cos(Math.toRadians(alpha + ANGLE_SPAN)) * Math.cos(Math.toRadians(beta + ANGLE_SPAN)));
float y2 = (float) (RADIAN * Math.sin(Math.toRadians(alpha + ANGLE_SPAN)) * Math.cos(Math.toRadians(beta + ANGLE_SPAN)));
float z2 = (float) (RADIAN * Math.sin(Math.toRadians(beta + ANGLE_SPAN)));
// 第三个点的坐标
float x3 = (float) (RADIAN * Math.cos(Math.toRadians(alpha)) * Math.cos(Math.toRadians(beta + ANGLE_SPAN)));
float y3 = (float) (RADIAN * Math.sin(Math.toRadians(alpha)) * Math.cos(Math.toRadians(beta + ANGLE_SPAN)));
float z3 = (float) (RADIAN * Math.sin(Math.toRadians(beta + ANGLE_SPAN)));
vertex.add(x0);
vertex.add(y0);
vertex.add(z0);
vertex.add(x1);
vertex.add(y1);
vertex.add(z1);
vertex.add(x2);
vertex.add(y2);
vertex.add(z2);
vertex.add(x0);
vertex.add(y0);
vertex.add(z0);
vertex.add(x2);
vertex.add(y2);
vertex.add(z2);
vertex.add(x3);
vertex.add(y3);
vertex.add(z3);
}
}
numVertex = vertex.size();
}
顶点着色器、片段着色器
新建顶点着色器和片段着色器
res/raw/vertex_shader.glsl
attribute vec4 aPosition;
uniform mat4 vMatrix;
void main() {
gl_Position = vMatrix * aPosition;
}
res/raw/fragment_shader.glsl
uniform vec4 uColor;
void main()
{
gl_FragColor = uColor;
}
给Ball类添加构造函数,编译链接程序,
package com.example.drawball;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import com.example.drawball.utils.BufferHelper;
import com.example.drawball.utils.Logit;
import com.example.drawball.utils.MatrixState;
import com.example.drawball.utils.ShaderHelper;
import com.example.drawball.utils.TextResourceReader;
import com.example.drawcircle.R;
import android.content.Context;
import android.opengl.GLES30;
public class Ball {
private static final String TAG = "Circle";
private Context context;
private FloatBuffer vertexBuffer;
private FloatBuffer colorBuffer;
private static final int BYTES_PER_FLOAT = 4; //浮点字节个数
private static final int COORS_PER_VERTEX = 3; // 顶点坐标分量个数
private static final int ANGLE_SPAN = 10; // 圆周划分角度10度
private static final float RADIANS = 0.5f;
private int numVertex;
private ArrayList<Float> vertex;
private int program; //应用程序句柄
private static final String A_POSITION = "aPosition";
private static final String U_COLOR = "uColor";
private static final String V_MATRIX = "vMatrix";
private int vMatrixLocation;
public Ball(Context context) {
this.context = context;
initVertex();
vertexBuffer = BufferHelper.getFloatBuffer(vertex);
getProgram();
int aPositionLocation = GLES30.glGetAttribLocation(program, A_POSITION); // 对应于 vertex_shader.glsl 中 attribute属性
Logit.d(TAG, "aPosition location: " + aPositionLocation);
GLES30.glVertexAttribPointer(aPositionLocation, COORS_PER_VERTEX, GLES30.GL_FLOAT, false, 0, vertexBuffer); // 区别glVertexAttribIPointer
GLES30.glEnableVertexAttribArray(aPositionLocation); //启用顶点属性数组
int uColorLocation = GLES30.glGetUniformLocation(program, U_COLOR); // 对应与 fragment_shader 中uniform属性
Logit.d(TAG, "uColor Location: " + uColorLocation);
GLES30.glUniform4f(uColorLocation, 1, 0, 0, 1); // 设置着色器中变量的值
vMatrixLocation = GLES30.glGetUniformLocation(program, V_MATRIX);
}
/**
* 初始化顶点数组
* angleSpan角度进行划分
* point3 point2 (beta + angleSpan)
* point0 point1 (alpha + angleSpan)
*/
private void initVertex() {
vertex = new ArrayList<Float>();
for (int alpha = 0; alpha <= 360; alpha += ANGLE_SPAN) {
for (int beta = -90; beta <= 90; beta += ANGLE_SPAN) {
// 第一个点的坐标
float x0 = (float) (RADIANS * Math.cos(Math.toRadians(alpha)) * Math.cos(Math.toRadians(beta)));
float y0 = (float) (RADIANS * Math.sin(Math.toRadians(alpha)) * Math.cos(Math.toRadians(beta)));
float z0 = (float) (RADIANS * Math.sin(Math.toRadians(beta)));
// 第二个点的坐标
float x1 = (float) (RADIANS * Math.cos(Math.toRadians(alpha + ANGLE_SPAN)) * Math.cos(Math.toRadians(beta)));
float y1 = (float) (RADIANS * Math.sin(Math.toRadians(alpha + ANGLE_SPAN)) * Math.cos(Math.toRadians(beta)));
float z1 = (float) (RADIANS * Math.sin(Math.toRadians(beta)));
// 第三个点的坐标
float x2 = (float) (RADIANS * Math.cos(Math.toRadians(alpha + ANGLE_SPAN)) * Math.cos(Math.toRadians(beta + ANGLE_SPAN)));
float y2 = (float) (RADIANS * Math.sin(Math.toRadians(alpha + ANGLE_SPAN)) * Math.cos(Math.toRadians(beta + ANGLE_SPAN)));
float z2 = (float) (RADIANS * Math.sin(Math.toRadians(beta + ANGLE_SPAN)));
// 第三个点的坐标
float x3 = (float) (RADIANS * Math.cos(Math.toRadians(alpha)) * Math.cos(Math.toRadians(beta + ANGLE_SPAN)));
float y3 = (float) (RADIANS * Math.sin(Math.toRadians(alpha)) * Math.cos(Math.toRadians(beta + ANGLE_SPAN)));
float z3 = (float) (RADIANS * Math.sin(Math.toRadians(beta + ANGLE_SPAN)));
vertex.add(x0);
vertex.add(y0);
vertex.add(z0);
vertex.add(x1);
vertex.add(y1);
vertex.add(z1);
vertex.add(x2);
vertex.add(y2);
vertex.add(z2);
vertex.add(x0);
vertex.add(y0);
vertex.add(z0);
vertex.add(x2);
vertex.add(y2);
vertex.add(z2);
vertex.add(x3);
vertex.add(y3);
vertex.add(z3);
}
}
numVertex = vertex.size();
}
private void getProgram() {
String vertexSource = TextResourceReader.readTextFileFromResource(context, R.raw.vertex_shader);
String fragmentSource = TextResourceReader.readTextFileFromResource(context, R.raw.fragment_shader);
// int vertexShader = ShaderHelper.compileVertexShader(vertexSource);
// int fragmentShader = ShaderHelper.compileFragmentShader(fragmentSource);
//获取program的id
program = ShaderHelper.buildProgram(vertexSource, fragmentSource);
Logit.d(TAG, "program: " + program);
GLES30.glUseProgram(program); // 不要忘记设置,否则不会起作用
}
public void draw() {
// GLES30.glUniform4f(2, 1, 0, 1, 0);
// GLES30.glDrawElements(GLES30.GL_TRIANGLES, 3, GLES30.GL_FLOAT, vertexBuffer);
GLES30.glUniformMatrix4fv(vMatrixLocation, 1, false, MatrixState.getFinalMatrix(), 0);
GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, numVertex);
}
}
设置相机位置和透视投影
添加BallRenderer类,设置相机和透视投影
src/com/example/drawball/BallRenderer.java
package com.example.drawball;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.opengl.GLES30;
import android.opengl.GLSurfaceView.Renderer;
import com.example.drawball.utils.Logit;
import com.example.drawball.utils.MatrixState;
public class BallRenderer implements Renderer{
private static final String TAG = "CircleRenderer";
private Ball ball;
private Context context;
public BallRenderer(Context context) {
// TODO Auto-generated constructor stub
this.context = context;
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// TODO Auto-generated method stub
Logit.d(TAG, "onSurfaceCreated.");
ball = new Ball(context);
GLES30.glClearColor(1, 1, 1, 1);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// TODO Auto-generated method stub
Logit.d(TAG, "onSurfaceChanded...");
// GLES30.glViewport(0, 0, width, height);
float ratio = 1.0f * width / height;//先转换成浮点数再做运算
MatrixState.setCamera(0, 0, 7, 0, 0, 0, 0, 1, 0);
MatrixState.setProjectOrtho(-ratio, ratio, -1, 1, 5, 9);
}
@Override
public void onDrawFrame(GL10 gl) {
// TODO Auto-generated method stub
Logit.d(TAG, "onDrawFrame");
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
ball.draw();
}
}
package com.example.drawball;
import android.app.Activity;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
public class BallActivity extends Activity {
private GLSurfaceView mTriGLView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_tri);
mTriGLView = new CircleGLSurfaceView(this);
setContentView(mTriGLView);
}
static class CircleGLSurfaceView extends GLSurfaceView {
public CircleGLSurfaceView(Context context) {
super(context);
setEGLContextClientVersion(3);
setRenderer(new BallRenderer(context));
setRenderMode(RENDERMODE_WHEN_DIRTY);
}
}
}
运行结果
渐变色球
将顶点位置作为颜色值传给片段着色器颜色值来绘制, 顶点着色器中添加varying 变量vColor存储顶点位置,传递给片段着色器, 如下:
顶点着色器、片段着色器
res/raw/vertex_shader1.glsl
attribute vec4 aPosition;
uniform mat4 vMatrix;
varying vec3 vColor;
void main() {
gl_Position = vMatrix * aPosition;
vColor = aPosition.xyz;
}
res/raw/fragment_shader1.glsl
uniform vec4 uColor;
varying vec3 vColor;
void main()
{
gl_FragColor = vec4(vColor, 1.0);
}
运行结果: