Android OpenGl Es 学习(二):定义顶点和着色器

概述

这是一个新的系列,学习OpengGl Es,其实是《OpenGl Es 应用开发实践指南 Android卷》的学习笔记,感兴趣的可以直接看这本书,当然这个会记录自己的理解,以下只作为笔记,以防以后忘记

之后会对本书的前九章依次分析记录

Android OpenGl Es 学习(一):创建一个OpenGl es程序

Android OpenGl Es 学习(二):定义顶点和着色器

Android OpenGl Es 学习(三):编译着色器

Android OpenGl Es 学习(四):增填颜色

Android OpenGl Es 学习(五):调整宽高比

Android OpenGl Es 学习(六):进入三维

Android OpenGl Es 学习(七):使用纹理

Android OpenGl Es 学习(八):构建简单物体

Android OpenGl Es 学习(九):增添触摸反馈

最终是要实现一个曲棍球的简单游戏,类似这样的

定义顶点

我们看到上面是最终实现的效果,首先我们先简单实现,一个矩形,一根线,俩个点

我们看到其实一个矩形是由4个顶点组成,然后把四个顶点连成线

点,直线,三角形

在OpenGl中只能绘制点,子线,三角形,也就是所我们不能直接绘制矩形,我们需要把矩形分解成若干三角形,然后在组成成矩形

如图所示,就把一个矩形划分为俩个三角形

在代码中定义顶点

在代码中这些顶点会用浮点数数组来表示,因为是二维坐标,所以每个顶点要用俩个浮点数来记录,一个标记x轴位置,一个标记y轴位置,这个数组通常被称为属性(attribute)数组

  float[] tableVertices = {
    
    
                //第一个三角
                0f, 0f,
                9f, 14f,
                0,14f,
                //第二个三角
                0f,0f,
                9f, 0f,
                9f, 14f,
        };

这个数组表示俩个三角形,每个三角形都以逆时针表示,一共四个顶点,俩个三角形共用俩个顶点

添加线和点

   float[] tableVertices = {
    
    
            //第一个三角
            0f, 0f,
            9f, 14f,
            0, 14f,
            //第二个三角
            0f, 0f,
            9f, 0f,
            9f, 14f,

            //线
            0f, 7f,
            9f, 7f,

            //点
            4.5f, 2f,
            4.5f, 12f
    };

让数据可以被opengl使用

上面我们已经定义好顶点了,但是我们的java代码是运行在虚拟机上,而opengl是运行在本地的硬件上的,那么如何才能把java数据可以让opengl使用呢?

  • 第一种就是JNI,如果需要了解可以看我之前的博客
  • 第二种就是改变内存的分配方式,java有一个特殊的类集合,可以分配本地的内存块,并且把java数据复制到本地内存

我们看下代码

private final int BYTES_PER_FLOAT = 4;
				
FloatBuffer verticeData = ByteBuffer.allocateDirect(tableVertices.length * BYTES_PER_FLOAT)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer()
                .put(tableVertices);
        verticeData.position(0);

每个浮点数有32位精度,而每个byte有8位精度,所以每个浮点数都占4个字节

方法 描述
ByteBuffer.allocateDirect 分配一块本地内存,分配大小由外部传入
order(ByteOrder.nativeOrder()) 告诉缓冲区,按照本地字节序组织内容
asFloatBuffer() 我们希望操作Float,调用这个方法会返回FloatBuffer
put() 填充数据
position() 把数据下标移动到指定位置

opengl管道

现在opengl已经拥有了数据,在把矩形画到屏幕之前,他们还需要在opengl的管道(pipeline)中传递,这一步就需要使用着色器(shader),这些着色器会告诉图形处理单元(CPU)如何绘制数据,有俩种着色器我们需要定义

  • 顶点着色器(vertex shader):生成每个顶点的最终位置,针对每个顶点他都会执行一次,一旦位置确定,opengl就可以把这些顶点组装成点,线和三角形
  • 片段着色器(fragment shader):为组成点,线,三角形的每个片段生成最终的颜色,针对每个片段他都会执行一次,一个片段是一个小的,单一颜色的长方形区域,类似计算机屏幕上的一个像素

一旦最终的颜色生成后,opengl会把他们写到一块称为帧缓冲区(frame buffer)的内存块中,然后Android会把这个帧缓冲区显示到屏幕上

光栅化技术

移动设备的显示屏由成千上百万个独立小组件组成,他们被称为像素,这些像素中每一个都有能力显示几百万颜色中的一种,其实每个像素都是由三个独立的子组件组成,他们发出红色,蓝色,绿色的光,因为每个像素很小,人的眼睛就会把,红绿蓝管混合,从而创造出巨量的颜色,只需要局狗多的像素就可以显示出你想要的画面

opengl通过光栅化的过程,把每个点,线,三角形,分解成大量的小片段,映射到移动设备的像素上,从而生成图像,这些小片段类似显示屏上的像素,每一个都包含单一的纯色,为了表示颜色,每个片段都有4个分量,红色,蓝色,绿色,来表示颜色,阿尔发(alpha)分量用来表示透明度

GLSL

要创建顶点着色器,首先要了解opengl的着色语言OpenGl Shader Language(GLSL),想要详学习换这个语言,可以参考这个OpenGL shader GLSL 中文手册
,下面记录一些基本信息

基本数据类型

类型 说明
void 空类型,不返回任何值
bool 布尔类型true,false
int 带符号的整数 signed integer
float 带符号的浮点数
vec2, vec3, vec4 n维浮点数向量,包括2,3,4个元素的浮点型向量
bvec2, bvec3, bvec4 n维布尔向量,包含2,3,4个元素的布尔向量
ivec2, ivec3, ivec4 n维整形向量,包含2,3,4个元素的整形向量
mat2, mat3, mat4 2✖️2,3✖️3,4✖️4,浮点数矩阵
sampler2D 2D纹理
samplerCube 立方体纹理

基本结构和数组

类型 描述
结构 struct type-name{} 类似c语言中的 结构体
数组 float foo[3] glsl只支持1维数组,数组可以是结构体的成员

变量限定符

修饰符 描述
none (默认可省略)本地变量可读可写,函数的入参就是这种变量
const 声明变量或函数的参数为只读类型
attribute 只能存在vertex shader(顶点着色器),一般用于保存顶点或法线数据,可以在缓冲区中读取数据
uniform 在运行时shader无法改变uniform变量,一版用来放置程序传递给shader的变换矩阵,材质,光照参数
varying 主要负责vertex和fragment之间传递数据

参数限定符

函数的参数默认是以拷贝的形式传递的,也就是值传递,任何传递给函数的变量,其值都会拷贝一份,然后再交给函数内部进行处理,我们也可以添加限定符,来达到引用传递的效果

类型 描述
默认 默认使用in限定符
in 真正传入函数的是参数的一份拷贝,在函数内修改参数值,不会影响参数变量本身
out 参数的值不会传递给函数,但是在函数内部修改值,会在函数结束后参数的值会改变
inout 传入函数的参数是引用,函数内部修改值,参数也会改变

函数

GLSL允许在程序的最外部声明函数,函数不能嵌套,不能递归调用,且必须声明返回值类型(无返回值时返回void),在其他方面与c函数一样

vec4 getPosition(){
    
     
    vec4 v4 = vec4(0.,0.,0.,1.);
    return v4;
}
 
void doubleSize(inout float size){
    
    
    size= size*2.0  ;
}
void main() {
    
    
    float psize= 10.0;
    doubleSize(psize);
    gl_Position = getPosition();
    gl_PointSize = psize;
}

类型转换

GLSL可以使用构造函数进行显示类型转换

bool t= true;
bool f = false;
 
int a = int(t); //true转换为1或1.0
int a1 = int(f);//false转换为0或0.0
 
float b = float(t);
float b1 = float(f);
 
bool c = bool(0);//0或0.0转换为false
bool c1 = bool(1);//非0转换为true
 
bool d = bool(0.0);
bool d1 = bool(1.0);

精度限制

GLSL在进行光栅化的时候,会进行大量的浮点运算,这些运算可能是设备不能承受的,所以GLSL提供了三种浮点精度,可以根据不同的设备选择不同的精度

在变量前加highp mediump lowp,即可对精度的声明

lowp float color;
varying mediump vec2 Coord;
lowp ivec2 foo(lowp mat3);
highp mat4 m;

除了精度限定符,我们还可以指定默认使用的精度,如果某个变量没有使用精度限定符,则会使用默认精度,默认精度限定符放在着色器代码初始位置,如:

precision highp float;//默认高精度float
precision mediump int;//默认中精度int

invariant关键字:

由于shader在编译的时候内部会做一些优化,可能导致同样的运算在不同的shader中结果不一致的问题,这会引起一些问题,尤其是在vertex shader向fragment shader传值的时候,所以我们需要使用invariant关键字要求计算结果必须一致,除了这个,我们也可以用#pragma STDGL invariant(all)命令来保证所有的输出一致,这样会限制编译器的优化程度,降低性能

#pragma STDGL invariant(all) //所有输出变量为 invariant
invariant varying texCoord; //varying在传递数据的时候声明为invariant

内置的特殊变量

GLSL使用一些特殊的内置变量与硬件进行沟通,他们大致分为俩种,一种是input类型,他们负责向硬件发送数据,另一种是output类型,负责向程序回传数据,方便编程需要

在vertex shader中

output的内置变量

变量 描述
highp vec4 gl_Position; gl_Position 放置顶点坐标信息
mediump float gl_PointSize; gl_PointSize 绘制点的大小

在fragment shader中

input类型的内置变量

变量 描述
mediump vec4 gl_FragCoord; 片元在framebuffer画面的相对位置
bool gl_FrontFacing; 标志当前图元是不是正面图元的一部分
mediump vec2 gl_PointCoord; 经过插值计算后的纹理坐标,点的范围是0.0到1.0

output类型内置变量

变量 描述
mediump vec4 gl_FragColor; 片元在framebuffer画面的相对位置
mediump vec4 gl_FragData[n] 设置当前片点的颜色,使用glDrawBuffers数据数组

内置函数

参考上面链接吧,这里就不继续写了

创建顶点着色器

首先我们在res新建一个raw文件夹,然后新建一个文件vertex_shader.glsl,然后用GLSL书写代码

 attribute vec4 a_Position;

  void main() {
      gl_Position =  a_Position;
      gl_PointSize=10.0;
   }

我们定义的每一个顶点,顶点着色器都会被调用一次,当他被调用时,他会在gl_Position属性接受当前顶点的位置

main函数是着色器的入口,他需要做的就是把前面定义的位置,赋值到指定输出变量gl_Position,这个着色器给gl_Position赋值,opengl会把gl_Position储存的值当做顶点的最终位置,然后把这些顶点组装成点,线,三角形

创建片段着色器

我们新建一个文件fragment_shader.glsl

 precision mediump float;
 uniform vec4 u_Color;
   void main() {
        gl_FragColor = u_Color;
    }

第一行设置精度

uniform 不像属性每个顶点都要设置一个,一个uniform会让每个顶点都使用同一个值,除非我们改变他们

main函数是着色器的入口,然后把uniform定义的颜色赋值到特殊的输出变量gl_FragColor

猜你喜欢

转载自blog.csdn.net/qq_34760508/article/details/108716294