学习OpenGL ES for Android(三)

上篇我们看到绘制点和线,在平面上,点和线组成了面,我们先看绘制三角形。

glDrawArrays的mode参数除了点和线,还有三角,是这三个:GL_TRIANGLE,GL_TRIANGLE_STRIP和GL_TRIANGLE_FAN。他们的绘制规则如下图所示,

GL_TRIANGLE:每三个顶点绘制为三角形,如果不足三个则不绘制,绘制的三角形个数是:顶点数/3
GL_TRIANGLE_STRIP:相邻的三个顶点就绘制为三角形,绘制的三角形个数为:顶点数-2
GL_TRIANGLE_FAN:绘制扇形,第一个顶点和其他两个相邻的顶点绘制三角形,绘制的三角形个数为:顶点数-2

绘制三角形的代码如下,

    @Override
    public void onDrawFrame(GL10 gl) {
        super.onDrawFrame(gl);
        ....//省略了前面的代码
        // 设置颜色
        GLES20.glUniform4fv(colorHandle, 1, color, 0);
        // 画三角:每间隔三个顶点结合为一个三角
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
        // 画三角:每相邻的三个顶点为一个三角
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 3, 6);
        // 画三角扇形
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 9, 6);
        GLES20.glDisableVertexAttribArray(positionHandle);
    }

源码地址:https://github.com/jklwan/OpenGLProject/blob/master/sample/src/main/java/com/chends/opengl/view/window/TriangleView.java

绘制矩形

和桌面不同的是,OpenGL ES没有绘制多边形的功能,绘制多边形可以使用三角形组合而成。

这里提一下glDrawElements( int mode, int count, int type, java.nio.Buffer indices),参数mode和glDrawArrays的相同;参数count是索引数组的总数;type是索引的数量;indices是索引集合;用glDrawElements绘制一个矩形可以定义四个顶点,然后定义索引数组{0,1,2,0,2,3}表示012和123分别绘制三角形而组合成矩形。下面看下两种绘制矩形的方式的代码

public class SquareRenderer extends BaseRenderer {

    private final float[] TriangleCoords = {
            -0.7f, 0.5f, 0.0f,
            -0.2f, 0.5f, 0.0f,
            -0.7f, -0.5f, 0.0f,
            -0.2f, -0.5f, 0.0f,

            0.2f, 0.5f, 0.0f,
            0.7f, 0.5f, 0.0f,
            0.2f, -0.5f, 0.0f,
            0.7f, -0.5f, 0.0f,
    };

    private short[] drawOrder = {
            4, 5, 6, // 第一个三角形
            5, 6, 7 // 第二个三角形
    };

    @Override
    public void onDrawFrame(GL10 gl) {
        super.onDrawFrame(gl);
        ....//省略部分代码
        // 设置颜色
        GLES20.glUniform4fv(colorHandle, 1, color, 0);
        // 绘制矩形
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
        ShortBuffer drawListBuffer = OpenGLUtil.createShortBuffer(drawOrder);
        // 绘制矩形
        GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
        GLES20.glDisableVertexAttribArray(positionHandle);
    }
}

效果图如下

改变三角形的颜色

改变顶点的颜色,需要修改顶点着色器代码,设置颜色参数以便传入,片段着色器中使用传入的颜色进行绘制,代码分别如下

        vertexShaderCode =
                "attribute vec3 aPosition;" +
                        "attribute vec3 aColor;" + // 颜色变量的属性位置值为 1
                        "varying vec3 ourColor;" +
                        "void main() {" +
                        "  gl_Position = vec4(aPosition, 1.0);" +
                        "   ourColor = aColor;" +  // 将ourColor设置为我们从顶点数据那里得到的输入颜色
                        "}";
        fragmentShaderCode =
                "precision mediump float;" +
                        "varying vec3 ourColor;" +
                        "void main() {" +
                        "  gl_FragColor = vec4(ourColor, 1.0);" +
                        "}"; // 动态改变颜色

我们同样使用glGetAttribLocation获取aColor变量的索引,然后传入我们的颜色值。绘制的代码如下

    @Override
    public void onDrawFrame(GL10 gl) {
        super.onDrawFrame(gl);
        int shaderProgram = OpenGLUtil.createProgram(vertexShaderCode, fragmentShaderCode);
        GLES20.glUseProgram(shaderProgram);
        int positionHandle = GLES20.glGetAttribLocation(shaderProgram, "aPosition");
        int colorHandle = GLES20.glGetAttribLocation(shaderProgram, "aColor");
        FloatBuffer vertexBuffer = OpenGLUtil.createFloatBuffer(vertices);
        GLES20.glEnableVertexAttribArray(positionHandle);
        vertexBuffer.position(0);
        // 传入顶点坐标
        GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT,
                false, (3 + 3) * 4, vertexBuffer);

        vertexBuffer.position(3);
        GLES20.glEnableVertexAttribArray(colorHandle);
        // 传入颜色
        GLES20.glVertexAttribPointer(colorHandle, 3, GLES20.GL_FLOAT,
                false, (3 + 3) * 4, vertexBuffer);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertices.length / (3 + 3));

        GLES20.glDisableVertexAttribArray(positionHandle);
        GLES20.glDisableVertexAttribArray(colorHandle);
    }

源码地址https://github.com/jklwan/OpenGLProject/blob/master/sample/src/main/java/com/chends/opengl/view/window/TriangleColorView.java

此篇文章我们学习了矩形的绘制和顶点颜色的变化,下一篇我们将要学习图形的变换。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个简单的Android OpenGL ES实现3D抛骰子的示例代码: 首先在build.gradle中添加OpenGL ES依赖: ```groovy implementation 'com.android.support:support-v4:28.0.0' implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.google.android.gms:play-services-gcm:16.0.0' implementation 'com.google.android.gms:play-services-ads:17.1.1' implementation 'com.google.android.gms:play-services-auth:16.0.1' implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'com.android.support:design:28.0.0' implementation 'com.android.support:cardview-v7:28.0.0' implementation 'com.android.support:recyclerview-v7:28.0.0' implementation 'com.google.code.gson:gson:2.8.5' implementation 'com.squareup.okhttp3:okhttp:3.11.0' implementation 'com.squareup.okio:okio:1.14.0' implementation 'com.github.bumptech.glide:glide:4.9.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0' implementation 'com.squareup.retrofit2:retrofit:2.5.0' implementation 'com.squareup.retrofit2:converter-gson:2.5.0' implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0' implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0' implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' implementation 'io.reactivex.rxjava2:rxjava:2.2.8' ``` 接着创建一个继承自GLSurfaceView的自定义视图: ```java public class DiceView extends GLSurfaceView implements Renderer { private final static int DICE_NUM = 3; // 骰子数量 private final static float DICE_SIZE = 0.5f; // 骰子大小 private final static float DICE_SPEED = 0.1f; // 骰子旋转速度 private Dice[] dices = new Dice[DICE_NUM]; // 骰子数组 private float xAngle = 0; // x轴旋转角度 private float yAngle = 0; // y轴旋转角度 private float zAngle = 0; // z轴旋转角度 public DiceView(Context context) { super(context); setRenderer(this); setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { for (int i = 0; i < DICE_NUM; i++) { dices[i] = new Dice(getContext(), DICE_SIZE, DICE_SPEED); } gl.glClearColor(0.5f, 0.5f, 0.5f, 1.0f); gl.glEnable(GL10.GL_DEPTH_TEST); gl.glEnable(GL10.GL_TEXTURE_2D); gl.glShadeModel(GL10.GL_SMOOTH); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { gl.glViewport(0, 0, width, height); gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadIdentity(); float ratio = (float) width / height; gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10); } @Override public void onDrawFrame(GL10 gl) { gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); GLU.gluLookAt(gl, 0, 0, 3, 0, 0, 0, 0, 1, 0); gl.glRotatef(xAngle, 1, 0, 0); gl.glRotatef(yAngle, 0, 1, 0); gl.glRotatef(zAngle, 0, 0, 1); for (int i = 0; i < DICE_NUM; i++) { dices[i].draw(gl); } } public void setAngles(float xAngle, float yAngle, float zAngle) { this.xAngle = xAngle; this.yAngle = yAngle; this.zAngle = zAngle; } } ``` 然后创建一个Dice类来表示骰子: ```java public class Dice { private FloatBuffer vertexBuffer; private FloatBuffer textureBuffer; private ByteBuffer indexBuffer; private int[] textures = new int[1]; private int[] diceValues = {1, 2, 3, 4, 5, 6}; private float diceSize; private float diceSpeed; private float angleX = 0; private float angleY = 0; private float angleZ = 0; private float[] vertices = { -0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, -0.5f, 0.5f, -0.5f, -0.5f, -0.5f, -0.5f, 0.5f, -0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, -0.5f, 0.5f }; private float[] textureCoords = { 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0 }; private byte[] indices = { 0, 1, 2, 0, 2, 3, 0, 3, 7, 0, 7, 4, 0, 4, 5, 0, 5, 1, 1, 5, 6, 1, 6, 2, 3, 2, 6, 3, 6, 7, 4, 7, 6, 4, 6, 5 }; public Dice(Context context, float diceSize, float diceSpeed) { this.diceSize = diceSize; this.diceSpeed = diceSpeed; ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4); vbb.order(ByteOrder.nativeOrder()); vertexBuffer = vbb.asFloatBuffer(); vertexBuffer.put(vertices); vertexBuffer.position(0); ByteBuffer tbb = ByteBuffer.allocateDirect(textureCoords.length * 4); tbb.order(ByteOrder.nativeOrder()); textureBuffer = tbb.asFloatBuffer(); textureBuffer.put(textureCoords); textureBuffer.position(0); indexBuffer = ByteBuffer.allocateDirect(indices.length); indexBuffer.put(indices); indexBuffer.position(0); Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.dice); GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); bitmap.recycle(); Random random = new Random(); setDiceValue(diceValues[random.nextInt(6)]); } public void draw(GL10 gl) { gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer); gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer); gl.glPushMatrix(); gl.glRotatef(angleX, 1, 0, 0); gl.glRotatef(angleY, 0, 1, 0); gl.glRotatef(angleZ, 0, 0, 1); gl.glDrawElements(GL10.GL_TRIANGLES, indices.length, GL10.GL_UNSIGNED_BYTE, indexBuffer); gl.glPopMatrix(); gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); } public void setDiceValue(int value) { angleX = 0; angleY = 0; angleZ = 0; switch (value) { case 1: angleX = 90; break; case 2: angleX = -90; break; case 3: angleY = 90; break; case 4: angleY = -90; break; case 5: angleZ = 90; break; case 6: angleZ = -90; break; } } public void update() { angleX += diceSpeed; angleY += diceSpeed; angleZ += diceSpeed; } } ``` 最后在Activity中使用DiceView即可: ```java public class MainActivity extends AppCompatActivity { private DiceView diceView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); diceView = new DiceView(this); setContentView(diceView); } @Override protected void onResume() { super.onResume(); diceView.onResume(); } @Override protected void onPause() { super.onPause(); diceView.onPause(); } @Override public boolean onTouchEvent(MotionEvent event) { float x = event.getX(); float y = event.getY(); switch (event.getAction()) { case MotionEvent.ACTION_MOVE: float dx = x - lastX; float dy = y - lastY; diceView.setAngles(dy * 0.5f, dx * 0.5f, 0); diceView.requestRender(); break; } lastX = x; lastY = y; return true; } } ``` 这样就实现了一个简单的Android OpenGL ES实现3D抛骰子的程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值