void main()
{
gl_FragColor = u_Color;
}
其中,gl_FragColor
变量就是 OpenGL 最终渲染出来的颜色的全局变量,而u_Color
就是我们定义的变量,通过在 Java 层绑定到 u_Color
变量并给它赋值,就会传递到 Native 层的gl_FragColor
中。
而第一行的mediump
指的就是片段着色器的精度了,有三种可选,这里用中等精度就行了。uniform
则表示该变量是不可变的了,也就是固定颜色了,目前显示固定颜色就好了。
编译 OpenGL 程序
明白了着色器的功能和光栅化技术之后,对渲染管线的流程也就更加清楚了,接下来就是编译 OpenGL 的程序了。
编译 OpenGL 程序基本流程如下:
- 编译着色器
- 创建 OpenGL 程序和着色器链接
- 验证 OpenGL 程序
- 确定使用 OpenGL 程序
编译着色器
创建新的文件编写着色器程序,然后再从文件以字符串的形式中读取文件内容。这样会比把着色器程序写成字符串的形式更加清晰。
当读取了着色器程序内容之后,就可以编译了。
// 编译顶点着色器
public static int compileVertexShader(String shaderCode) {
return compileShader(GL_VERTEX_SHADER, shaderCode);
}
// 编译片段着色器
public static int compleFragmentShader(String shaderCode) {
return compileShader(GL_FRAGMENT_SHADER, shaderCode);
}
// 根据类型编译着色器
private static int compileShader(int type, String shaderCode) {
// 根据不同的类型创建着色器 ID
final int shaderObjectId = glCreateShader(type);
if (shaderObjectId == 0) {
return 0;
}
// 将着色器 ID 和着色器程序内容连接
glShaderSource(shaderObjectId, shaderCode);
// 编译着色器
glCompileShader(shaderObjectId);
// 以下为验证编译结果是否失败
final int[] compileStatsu = new int[1];
glGetShaderiv(shaderObjectId, GL_COMPILE_STATUS, compileStatsu, 0);
if ((compileStatsu[0] == 0)) {
// 失败则删除
glDeleteShader(shaderObjectId);
return 0;
}
return shaderObjectId;
}
以上程序主要就是通过glCreateShader
方法创建了着色器 ID,然后通过glShaderSource
连接上着色器程序内容,接下来通过glCompileShader
编译着色器,最后通过glGetShaderiv
验证是否失败。
glGetShaderiv
函数比较通用,在着色器阶段和 OpenGL 程序阶段都会通过它来验证结果。
创建 OpenGL 程序和着色器链接
接下来就是创建 OpenGL 程序并加着色器加进来。
public static int linkProgram(int vertexShaderId, int fragmentShaderId) {
// 创建 OpenGL 程序 ID
final int programObjectId = glCreateProgram();
if (programObjectId == 0) {
return 0;
}
// 链接上 顶点着色器
glAttachShader(programObjectId, vertexShaderId);
// 链接上 片段着色器
glAttachShader(programObjectId, fragmentShaderId);
// 链接着色器之后,链接 OpenGL 程序
glLinkProgram(programObjectId);
final int[] linkStatus = new int[1];
// 验证链接结果是否失败
glGetProgramiv(programObjectId, GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] == 0) {
// 失败则删除 OpenGL 程序
glDeleteProgram(programObjectId);
return 0;
}
return programObjectId;
}
首先通过glCreateProgram
程序创建 OpenGL 程序,然后通过glAttachShader
将着色器程序 ID 添加上 OpenGL 程序,接下来通过glLinkProgram
链接 OpenGL 程序,最后通过glGetProgramiv
来验证链接是否失败。
验证 OpenGL 程序
链接了 OpenGL 程序后,就是验证 OpenGL 是否可用。
public static boolean validateProgram(int programObjectId) {
glValidateProgram(programObjectId);
final int[] validateStatus = new int[1];
glGetProgramiv(programObjectId, GL_VALIDATE_STATUS, validateStatus, 0);
return validateStatus[0] != 0;
}
通过glValidateProgram
函数验证,并再次通过glGetProgramiv
函数验证是否失败。
确定使用 OpenGL 程序
当一切完成后,就是确定使用该 OpenGL 程序了。
// 创建 OpenGL 程序过程
public static int buildProgram(Context context, int vertexShaderSource, int fragmentShaderSource) {
int program;
int vertexShader = compileVertexShader(
TextResourceReader.readTextFileFromResource(context, vertexShaderSource));
int fragmentShader = compleFragmentShader(
TextResourceReader.readTextFileFromResource(context, fragmentShaderSource));
program = linkProgram(vertexShader, fragmentShader);
validateProgram(program);
return program;
}
// 创建完毕后,确定使用
mProgram = ShaderHelper.buildProgram(context, R.raw.point_vertex_shader
, R.raw.point_fragment_shader);
glUseProgram(mProgram);
将上述的过程合并起来,buildProgram
函数返回的 OpenGL 程序 ID 即可,通过glUseProgram
函数表示使用该 OpenGL 程序。
绘制
完成了 OpenGL 程序的编译,就是最后的绘制了,再回到渲染器 Renderer
里面。
public class PointRenderer extends BaseRenderer {
private Point mPoint;
public PointRenderer(Context mContext) {
super(mContext);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
super.onSurfaceCreated(gl, config);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
// 在 onSurfaceCreated 里面初始化,否则会报线程错误
mPoint = new Point(mContext);
// 绑定相应的顶点数据
mPoint.bindData();
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// 确定视口大小
glViewport(0, 0, width, height);
}
@Override
public void onDrawFrame(GL10 gl) {
// 清屏
glClear(GL_COLOR_BUFFER_BIT);
// 绘制
mPoint.draw();
}
}
在onSurfaceCreated
函数里面做初始化工作,绑定数据等;在onSurfaceChanged
方法里面确定视图大小,在onDrawFrame
里面执行绘制。
为了简化渲染流程,把所有的操作都放在放在要渲染的对象里面去了,声明一个 Point 对象,代表要绘制的点。
public class Point extends BaseShape {
// 着色器中定义的变量,在 Java 层绑定并赋值
private static final String U_COLOR = “u_Color”;
private static final String A_POSITION = “a_Position”;
private int aColorLocation;
private int aPositionLocation;
float[] pointVertex = {
0f, 0f
};
public Point(Context context) {
super(context);
mProgram = ShaderHelper.buildProgram(context, R.raw.point_vertex_shader
, R.raw.point_fragment_shader);
glUseProgram(mProgram);
vertexArray = new VertexArray(pointVertex);
POSITION_COMPONENT_COUNT = 2;
}
@Override
public void bindData() {
//绑定值
aColorLocation = glGetUniformLocation(mProgram, U_COLOR);
aPositionLocation = glGetAttribLocation(mProgram, A_POSITION);
// 给绑定的值赋值,也就是从顶点数据那里开始读取,每次读取间隔是多少
vertexArray.setVertexAttribPointer(0, aPositionLocation, POSITION_COMPONENT_COUNT,
0);
}
@Override
public void draw() {
// 给绑定的值赋值
glUniform4f(aColorLocation, 0.0f, 0.0f, 1.0f, 1.0f);
glDrawArrays(GL_POINTS, 0, 1);
}
}
在 Point 的构造函数中,编译并使用 OpenGL 程序,而在 bindData
函数中,通过glGetUniformLocation
和glGetAttribLocation
函数绑定了我们在 OpenGL 中声明的变量u_Color
和a_Position
,要注意的是,attribute
类型和uniform
类型所对应的方法是不同的,最后通过给POSITION_COMPONENT_COUNT
变量赋值,指定每个顶点数据的个数为 2 。
绑定了变量之后,接下来就是给他们赋值了,对于uniform
类型变量,由于是固定值,所以直接调用glUniform4f
方法给其赋值就好了,而attribute
类型变量,则需要对应顶点数据中的值了,vertexArray.setVertexAttribPointer
方法就是完成这个任务的。
// 给某个顶点数据绑定值,并 Enable 使能
public void setVertexAttribPointer(int dataOffset, int attributeLocation, int componentCount, int stride) {
floatBuffer.position(dataOffset);
glVertexAttribPointer(attributeLocation, componentCount, GL_FLOAT, false, stride, floatBuffer);
glEnableVertexAttribArray(attributeLocation);
floatBuffer.position(0);
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
总结
本文讲解了我对Android开发现状的一些看法,也许有些人会觉得我的观点不对,但我认为没有绝对的对与错,一切交给时间去证明吧!愿与各位坚守的同胞们互相学习,共同进步!
本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录
一个人可以走的很快,但一群人才能走的更远。如果你从事以下工作或对以下感兴趣,欢迎戳这里加入程序员的圈子,让我们一起学习成长!
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算文章来源地址https://www.toymoban.com/news/detail-844481.html
的圈子,让我们一起学习成长!**](https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0)文章来源:https://www.toymoban.com/news/detail-844481.html
AI人工智能、Android移动开发、AIGC大模型、C C#、Go语言、Java、Linux运维、云计算、MySQL、PMP、网络安全、Python爬虫、UE5、UI设计、Unity3D、Web前端开发、产品经理、车载开发、大数据、鸿蒙、计算机网络、嵌入式物联网、软件测试、数据结构与算法、音视频开发、Flutter、IOS开发、PHP开发、.NET、安卓逆向、云计算
到了这里,关于OpenGL 学习系列--基础的绘制流程,目前最稳定和高效的UI适配方案的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!