Opengl ES之三角形绘制
在前面我们已经在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)]
上代码
这里需要说明亮点:
- 在后续的实战例子中,经常会复用到前面介绍的demo的代码,因此如果是复用之前的代码逻辑,为了节省篇幅,笔者就不重复贴了。
- 在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之三角形绘制相关推荐
- android平台下OpenGL ES 3.0绘制圆点、直线和三角形
OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...
- android平台下OpenGL ES 3.0绘制纯色背景
OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...
- OpenGL ES for Android 绘制旋转的地球
No 图 No Code,我们先来欣赏下旋转的地球: 是不是很酷炫,要想绘制出上面酷炫的效果需要3个步骤: 计算球体顶点数据 地球纹理贴图 通过MVP矩阵旋转地球 计算球体顶点数据 我们知道OpenG ...
- android平台下OpenGL ES 3.0绘制立方体的几种方式
OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始 android平台下OpenGL ES 3.0绘制纯色背景 android平台下OpenGL ES 3.0绘 ...
- OpenGL ES for Android 绘制立方体
立方体有6个面,8个顶点,因此绘制立方体其实就是绘制6个面. 顶点shader attribute vec4 a_Position; attribute vec4 a_color; varying v ...
- Android OpenGL ES 2.0绘制简单三角形
实现步骤 l 实现一个工具类ShalderUtil,用于将着色器代码加载进显卡进行编译 l 实现一个三角形Triangle类 在该类中加载着色器.初始化顶点数据.初始化着色器以及绘制三角形方法 l ...
- Android中用OpenGL ES Tracer分析绘制过程
Tracer for OpenGL ES(http://developer.android.com/tools/help/gltracer.html)是Android SDK中新增加的开发工具,可逐帧 ...
- android 使用OPENGL ES实现三角形纹理贴图效果-纹理映射基础
效果图:...... 编写Dad.java *在Dad构造器中创建和设置场景渲染器为主动渲染,并设置重写触屏时间回调方法以记录触控笔坐标,改变三角形坐标系的位置,使三角形能够在场景中转动 *为声明场景 ...
- Opegnl ES之四边形绘制
四边形的绘制在Opengl ES是很重要的一项技巧,比如做视频播放器时视频的渲染就需要使用到Opengl ES绘制四边形的相关知识.然而在Opengl ES却没有直接提供 绘制四边形的相关函数,那么如 ...
最新文章
- 如何正确选择聚类算法? | CSDN博文精选
- 聚沙成塔 : 第十六届智能车竞赛规则你一言,我一语
- 经常使用的时间同步server地址
- Spring-AOP 通过配置文件实现 环绕增强
- 解决phoenix中创建的表名及字段默认是大写的问题
- php怎么取随机3位数字,使用php怎么从指定数字中获取随机组合
- 【华为云技术分享】ARM体系结构基础(2)
- 小白学python之整型,布尔值,十进制二进制转换和字符串详解for循环!
- SpringBoot中修改tomcat最大连接数、最大线程数、最大等待数
- Latex个人常用清单--不断更新
- @ OutputCache 指令的 VaryByCustom 属性来缓存不同版本的页面
- JavaScript学习笔记 及 JAVAScript优化
- 最简单的的树莓派安装opencv教程(一键安装)
- qca9535 tftp32 刷机_【U-Boot】U-Boot 刷机方法大全
- 硬盘分区故障修复全攻略
- 洛谷 P4578 [FJOI2018] Upc6605 福建OI2018 所罗门王的宝藏
- Desktoppr与 Dropbox国内成功使用
- Wireshark TS | 丢包?不要轻易下结论续
- simnow账户无法使用,simnow账户修改密码
- bzoj3772: 精神污染
热门文章
- Linux下raise函数,信号发送函数kill()和raise()
- 服务器插网线显示没插,求助贴 插网线连不上网
- mysql初始化命令_mysql初始化命令及其他命令
- java死锁代码演示
- 关于DDR协议一些操作的理解1
- oracle 修改表空间大小
- 关于创建涂鸦UI以及保存涂鸦图片的代码
- 【量化交易】量化导论金融基础理论
- LNMP架构环境搭建流程很详细
- Spark SQL入门:创建SparkSession时import spark.implicits._ 报错: error: value implicits is not a member of...