在前面我们已经在NDK层搭建好了EGL环境,也介绍了一些着色器相关的理论知识,那么这次我们就使用已经搭配的EGL绘制一个三角形吧。

在Opengl ES的世界中,无论多复杂的形状都是由点、线或三角形组成的。因此三角形的绘制在Opengl ES中相当重要,犹比武林高手的内功心法…

坐标系

在Opengl ES中有很多坐标系,今天我们首先了解一些标准化的设备坐标。

标准化设备坐标(Normalized Device Coordinates, NDC),一旦你的顶点坐标已经在顶点着色器中处理过,它们就是标准化设备坐标了,
标准化设备坐标是一个x、y和z的值都在-1.0到1.0的之间,任何落在-1和1范围外的坐标都会被丢弃/裁剪,不会显示在你的屏幕上。

如下图,在在标准化设备坐标中,假设有一个正方形的屏幕,那么屏幕中心就是坐标原点,左上角就是坐标(-1,1),右下角则是坐标(1,-1)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zRdcwgtu-1662517619843)(https://flyer-blog.oss-cn-shenzhen.aliyuncs.com/%E6%A0%87%E5%87%86%E5%8C%96%E8%AE%BE%E5%A4%87%E5%9D%90%E6%A0%87.png)]

上代码

这里需要说明亮点:

  1. 在后续的实战例子中,经常会复用到前面介绍的demo的代码,因此如果是复用之前的代码逻辑,为了节省篇幅,笔者就不重复贴了。
  2. 在demo中为了简洁,并没有开启子线程作为GL线程,很明显这是不对,实际开发中都应该开启子线程对Opengl进行操作。

首先为了后续方便使用,我们在Java层和C++分别创建一个BaseOpengl的基类:

BaseOpengl.javapublic class BaseOpengl {// 三角形public static final int DRAW_TYPE_TRIANGLE = 0;public long glNativePtr;protected EGLHelper eglHelper;protected int drawType;public BaseOpengl(int drawType) {this.drawType = drawType;this.eglHelper = new EGLHelper();}public void surfaceCreated(Surface surface) {eglHelper.surfaceCreated(surface);}public void surfaceChanged(int width, int height) {eglHelper.surfaceChanged(width,height);}public void surfaceDestroyed() {eglHelper.surfaceDestroyed();}public void release(){if(glNativePtr != 0){n_free(glNativePtr,drawType);glNativePtr = 0;}}public void onGlDraw(){if(glNativePtr == 0){glNativePtr = n_gl_nativeInit(eglHelper.nativePtr,drawType);}if(glNativePtr != 0){n_onGlDraw(glNativePtr,drawType);}}// 绘制private native void n_onGlDraw(long ptr,int drawType);protected native long n_gl_nativeInit(long eglPtr,int drawType);private native void n_free(long ptr,int drawType);}

下面是C++的BaseOpengl:

BaseOpengl.h#ifndef NDK_OPENGLES_LEARN_BASEOPENGL_H
#define NDK_OPENGLES_LEARN_BASEOPENGL_H
#include "../eglhelper/EglHelper.h"
#include "GLES3/gl3.h"
#include <string>class BaseOpengl {
public:EglHelper *eglHelper;GLint program{0};public:BaseOpengl();// 析构函数必须是虚函数virtual ~BaseOpengl();// 加载着色器并链接成程序void initGlProgram(std::string ver,std::string fragment);// 绘制virtual void onDraw() = 0;
};#endif //NDK_OPENGLES_LEARN_BASEOPENGL_H

注意基类的析构函数一定要是虚函数,为什么?如果不是虚函数的话则会导致无法完全析构,具体原因请大家面向搜索引擎编程。

BaseOpengl.cpp#include "BaseOpengl.h"
#include "../utils/ShaderUtils.h"BaseOpengl::BaseOpengl() {}void BaseOpengl::initGlProgram(std::string ver, std::string fragment) {program = createProgram(ver.c_str(),fragment.c_str());
}BaseOpengl::~BaseOpengl(){eglHelper = nullptr;if(program != 0){glDeleteProgram(program);}
}

然后使用BaseOpengl自定义一个SurfaceView,为MyGLSurfaceView:

public class MyGLSurfaceView extends SurfaceView implements SurfaceHolder.Callback {public BaseOpengl baseOpengl;private OnDrawListener onDrawListener;public MyGLSurfaceView(Context context) {this(context,null);}public MyGLSurfaceView(Context context, AttributeSet attrs) {super(context, attrs);getHolder().addCallback(this);}public void setBaseOpengl(BaseOpengl baseOpengl) {this.baseOpengl = baseOpengl;}public void setOnDrawListener(OnDrawListener onDrawListener) {this.onDrawListener = onDrawListener;}@Overridepublic void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {if(null != baseOpengl){baseOpengl.surfaceCreated(surfaceHolder.getSurface());}}@Overridepublic void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int w, int h) {if(null != baseOpengl){baseOpengl.surfaceChanged(w,h);}if(null != onDrawListener){onDrawListener.onDrawFrame();}}@Overridepublic void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {if(null != baseOpengl){baseOpengl.surfaceDestroyed();}}public interface OnDrawListener{void onDrawFrame();}
}

有了以上基类,既然我们的目标是绘制一个三角形,那么我们在Java层和C++层再新建一个TriangleOpengl的类吧,他们都继承TriangleOpengl:

TriangleOpengl.javapublic class TriangleOpengl extends BaseOpengl{public TriangleOpengl() {super(BaseOpengl.DRAW_TYPE_TRIANGLE);}}

C++ TriangleOpengl类,TriangleOpengl.h:


#ifndef NDK_OPENGLES_LEARN_TRIANGLEOPENGL_H
#define NDK_OPENGLES_LEARN_TRIANGLEOPENGL_H
#include "BaseOpengl.h"class TriangleOpengl: public BaseOpengl{
public:TriangleOpengl();virtual ~TriangleOpengl();virtual void onDraw();private:GLint positionHandle{-1};GLint colorHandle{-1};
};#endif //NDK_OPENGLES_LEARN_TRIANGLEOPENGL_H

TriangleOpengl.cpp:


#include "TriangleOpengl.h"
#include "../utils/Log.h"// 定点着色器
static const char *ver = "#version 300 es\n""in vec4 aColor;\n""in vec4 aPosition;\n""out vec4 vColor;\n""void main() {\n""    vColor = aColor;\n""    gl_Position = aPosition;\n""}";// 片元着色器
static const char *fragment = "#version 300 es\n""precision mediump float;\n""in vec4 vColor;\n""out vec4 fragColor;\n""void main() {\n""    fragColor = vColor;\n""}";// 三角形三个顶点
const static GLfloat VERTICES[] = {0.0f,0.5f,-0.5f,-0.5f,0.5f,-0.5f
};// rgba
const static GLfloat COLOR_ICES[] = {0.0f,0.0f,1.0f,1.0f
};TriangleOpengl::TriangleOpengl():BaseOpengl() {initGlProgram(ver,fragment);positionHandle = glGetAttribLocation(program,"aPosition");colorHandle = glGetAttribLocation(program,"aColor");LOGD("program:%d",program);LOGD("positionHandle:%d",positionHandle);LOGD("colorHandle:%d",colorHandle);
}TriangleOpengl::~TriangleOpengl() noexcept {}void TriangleOpengl::onDraw() {LOGD("TriangleOpengl onDraw");glClearColor(0.0f, 1.0f, 0.0f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);glUseProgram(program);/*** size 几个数字表示一个点,显示是两个数字表示一个点* normalized 是否需要归一化,不用,这里已经归一化了* stride 步长,连续顶点之间的间隔,如果顶点直接是连续的,也可填0*/glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,0,VERTICES);// 启用顶点数据glEnableVertexAttribArray(positionHandle);// 这个不需要glEnableVertexAttribArrayglVertexAttrib4fv(colorHandle, COLOR_ICES);glDrawArrays(GL_TRIANGLES,0,3);glUseProgram(0);// 禁用顶点glDisableVertexAttribArray(positionHandle);if(nullptr != eglHelper){eglHelper->swapBuffers();}LOGD("TriangleOpengl onDraw--end");
}

在前面的章节中我们介绍了着色器的创建、编译、链接等,但是缺少了具体使用方式,这里我们补充说明一下。

着色器的使用只要搞懂如何传递数据给着色器中变量。首先我们需要获取到着色器程序中的变量,然后赋值。

我们看上面的TriangleOpengl.cpp的构造函数:

TriangleOpengl::TriangleOpengl():BaseOpengl() {initGlProgram(ver,fragment);// 获取aPosition变量positionHandle = glGetAttribLocation(program,"aPosition");// 获取aColorcolorHandle = glGetAttribLocation(program,"aColor");LOGD("program:%d",program);LOGD("positionHandle:%d",positionHandle);LOGD("colorHandle:%d",colorHandle);
}

由上,我们通过函数glGetAttribLocation获取了变量aPosition和aColor的句柄,这里我们定义的aPosition和aColor是向量变量,如果我们定义的是uniform统一变量的话,则需要使用函数glGetUniformLocation获取统一变量句柄。
有了这些变量句柄,我们就可以通过这些变量句柄传递函数给着色器程序了,具体可参考TriangleOpengl.cpp的onDraw函数。

此外如果变量是一个统一变量(uniform)的话,则通过一系列的 glUniform...函数传递参数。

这里说明一下函数glVertexAttribPointer的stride参数,一般情况下不会用到,传递0即可,但是如果需要提高性能,例如将顶点坐标和纹理/颜色坐标等放在同一个数组中传递,则需要使用到这个stride参数了,目前顶点坐标数组和其他数组是分离的,暂时可以不管。

在Activity中调用一下测试结果:

public class DrawTriangleActivity extends AppCompatActivity {private TriangleOpengl mTriangleOpengl;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_draw_triangle);MyGLSurfaceView glSurfaceView = findViewById(R.id.my_gl_surface_view);mTriangleOpengl = new TriangleOpengl();glSurfaceView.setBaseOpengl(mTriangleOpengl);glSurfaceView.setOnDrawListener(new MyGLSurfaceView.OnDrawListener() {@Overridepublic void onDrawFrame() {mTriangleOpengl.onGlDraw();}});}@Overrideprotected void onDestroy() {if(null != mTriangleOpengl){mTriangleOpengl.release();}super.onDestroy();}
}

如果运行起来,看到一个蓝色的三角形,则说明三角形绘制成功啦!
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cUVRgRr3-1662517619844)(https://flyer-blog.oss-cn-shenzhen.aliyuncs.com/opengl%E8%93%9D%E8%89%B2%E4%B8%89%E8%A7%92%E5%BD%A2.png)]

源码

想来还是不贴源码链接了,纸上得来终觉浅,绝知此事要躬行。很多时候就是这样,你看着觉得很简单,实际如何还得动手敲,只有在敲的过程中出了问题,然后你解决了,只是才算是你的。

在这个系列完毕后再贴出整个项目demo的代码吧。。。

往期笔记

OpenglEs之EGL环境搭建
OpenglEs之着色器

关注我,一起进步,人生不止coding!!!

Opengl ES之三角形绘制相关推荐

  1. android平台下OpenGL ES 3.0绘制圆点、直线和三角形

    OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...

  2. android平台下OpenGL ES 3.0绘制纯色背景

    OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...

  3. OpenGL ES for Android 绘制旋转的地球

    No 图 No Code,我们先来欣赏下旋转的地球: 是不是很酷炫,要想绘制出上面酷炫的效果需要3个步骤: 计算球体顶点数据 地球纹理贴图 通过MVP矩阵旋转地球 计算球体顶点数据 我们知道OpenG ...

  4. android平台下OpenGL ES 3.0绘制立方体的几种方式

    OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...

  5. OpenGL ES for Android 绘制立方体

    立方体有6个面,8个顶点,因此绘制立方体其实就是绘制6个面. 顶点shader attribute vec4 a_Position; attribute vec4 a_color; varying v ...

  6. Android OpenGL ES 2.0绘制简单三角形

    实现步骤 l  实现一个工具类ShalderUtil,用于将着色器代码加载进显卡进行编译 l  实现一个三角形Triangle类 在该类中加载着色器.初始化顶点数据.初始化着色器以及绘制三角形方法 l ...

  7. Android中用OpenGL ES Tracer分析绘制过程

    Tracer for OpenGL ES(http://developer.android.com/tools/help/gltracer.html)是Android SDK中新增加的开发工具,可逐帧 ...

  8. android 使用OPENGL ES实现三角形纹理贴图效果-纹理映射基础

    效果图:...... 编写Dad.java *在Dad构造器中创建和设置场景渲染器为主动渲染,并设置重写触屏时间回调方法以记录触控笔坐标,改变三角形坐标系的位置,使三角形能够在场景中转动 *为声明场景 ...

  9. Opegnl ES之四边形绘制

    四边形的绘制在Opengl ES是很重要的一项技巧,比如做视频播放器时视频的渲染就需要使用到Opengl ES绘制四边形的相关知识.然而在Opengl ES却没有直接提供 绘制四边形的相关函数,那么如 ...

最新文章

  1. 如何正确选择聚类算法? | CSDN博文精选
  2. 聚沙成塔 : 第十六届智能车竞赛规则你一言,我一语
  3. 经常使用的时间同步server地址
  4. Spring-AOP 通过配置文件实现 环绕增强
  5. 解决phoenix中创建的表名及字段默认是大写的问题
  6. php怎么取随机3位数字,使用php怎么从指定数字中获取随机组合
  7. 【华为云技术分享】ARM体系结构基础(2)
  8. 小白学python之整型,布尔值,十进制二进制转换和字符串详解for循环!
  9. SpringBoot中修改tomcat最大连接数、最大线程数、最大等待数
  10. Latex个人常用清单--不断更新
  11. @ OutputCache 指令的 VaryByCustom 属性来缓存不同版本的页面
  12. JavaScript学习笔记 及 JAVAScript优化
  13. 最简单的的树莓派安装opencv教程(一键安装)
  14. qca9535 tftp32 刷机_【U-Boot】U-Boot 刷机方法大全
  15. 硬盘分区故障修复全攻略
  16. 洛谷 P4578 [FJOI2018] Upc6605 福建OI2018 所罗门王的宝藏
  17. Desktoppr与 Dropbox国内成功使用
  18. Wireshark TS | 丢包?不要轻易下结论续
  19. simnow账户无法使用,simnow账户修改密码
  20. bzoj3772: 精神污染

热门文章

  1. Linux下raise函数,信号发送函数kill()和raise()
  2. 服务器插网线显示没插,求助贴 插网线连不上网
  3. mysql初始化命令_mysql初始化命令及其他命令
  4. java死锁代码演示
  5. 关于DDR协议一些操作的理解1
  6. oracle 修改表空间大小
  7. 关于创建涂鸦UI以及保存涂鸦图片的代码
  8. 【量化交易】量化导论金融基础理论
  9. LNMP架构环境搭建流程很详细
  10. Spark SQL入门:创建SparkSession时import spark.implicits._ 报错: error: value implicits is not a member of...