Android OpenGL ES 2.0 手把手教学(3)- 顶点着色器 vertex shader

你们好,下面和大学一块儿学习如何使用顶点着色器vertex shader来作顶点变换,在个人github上有一个项目OpenGLES2.0SamplesForAndroid,我会不断地编写学习样例,文章和代码同步更新,欢迎关注,连接:github.com/kenneycode/…git

在以前的例子中,咱们都使用到了顶点着色器vertex shader,但只是简单地用了一下,把输入的顶点坐标又原样地输出了,没有作任何操做,这篇文章给你们介绍如何在vertex shader作顶点变换。github

咱们先看了解一下OpenGL的渲染管线(pipeline): 编程

这张图展现了咱们调用OpenGL的 drawXXX()方法后执行的流程,咱们传递的顶点首先会通过顶点着色器 vertex shader的处理,通常会在里面作顶点变换相关的逻辑,而后进行图元装配,再通过几何着色器 geometry shader,这个着色器相对来讲使用得少一些,可暂时先忽略,而后接下来就是光栅化,所谓光栅化就是把咱们要渲染的图像打碎成屏幕上的像素,由于最终要显示到屏幕上,就必须将图形对应到像素上,光栅化完成后,咱们就有了要渲染的图形对应的像素,此时像素尚未颜色,须要咱们填上颜色,这时就到达到了片断着色器 fragment shader,在 fragment shader中咱们一般进行颜色的计算,肯定对应的像素显示什么颜色, fragment shader将在下篇文章中介绍。

在整个渲染管线中,vertex shadergeometry shaderfragment shader这三部分是可编程分部,可编写shader代码实现相应的功能,咱们目前重点关注vertex shaderfragment shaderbash

这里特别注意一点,咱们的shader代码并非像普通程序那样,一次性输入全部顶点,而后再输出,例如对于vertex shader,咱们传递了3个顶点,并非3个顶点一块儿执行一次vertex shader,而是分别对这3个顶点执行一次,也就是执行了3次。对于fragment shader也是相似的,并非执行一次为全部的像素填充颜色,而是对每一个像素都执行一次。这个特色有时让初学者感到困惑。编程语言

先来回顾一下咱们简单的vertex shaderpost

precision mediump float;
attribute vec4 a_Position;
void main() {
    gl_Position = a_Position;
}
复制代码

第一行,咱们声明了这个shader使用的精度,mediump表示使用中等精度,一些对精度要求很高的应用,能够声明高精度。学习

第二行,咱们声明了一个attribute vec4 a_Position变量,它代表这是一个attribute类型的四维向量,什么attribute类型?上文提到若是咱们传递了3个顶点,就会对这3个顶点分别执行一次vertex shader,在一次执行中,这个attribute类型的变量所对应的就是这3个顶点中的某个顶点,与此相对的是uniform变量,uniform变量在全部vertex shader的执行过程当中都是同一个值,在本文中咱们也会遇到。ui

这里为何一个顶点是四维向量?咱们不是只传了二维的x、y坐标吗?在OpenGL中,顶点老是四维的,即x、y、z、w,其中x、y、z不传的话默认是0,w不传的话默认是1,w是用来作归一化(标准化)的,后续文章会有介绍。spa

回看咱们以前《Android OpenGL ES 2.0 手把手教学(1)- Hello World!》例子,咱们有这样一句:3d

// 指定a_Position所使用的顶点数据
// Specify the vertex data of a_Position
GLES20.glVertexAttribPointer(location, 2, GLES20.GL_FLOAT, false,0, buffer)
复制代码

其中第2个参数就是指定了一个顶点有多少个成份,所以在vertex shader中,vec4 a_Position接受的只有x、yzw保持默认值。

咱们继续往下看,vertex shader中包含一个main()方法做为入口,和不少编程语言相似。gl_Positionvertex shader的一个内置变量,表示vertex shader的输出,在咱们以前的例子,是直接将输入的顶点原样又输出了,本文将对顶点作变换,先看个简单的例子:

precision mediump float;
attribute vec4 a_Position;
void main() {
    gl_Position = a_Position + vec4(0.3, 0.3, 0, 0);
}
复制代码

咱们给顶点加上了一个偏移量,来看看效果:

能够看到,三角形发生的偏移,接下来,咱们来完善一下,将平移、缩放和旋转一块儿写到vertex shader中,为了计算上的方便,咱们使用平移矩阵、缩放矩阵和旋转矩阵,这些矩阵的写法是数学上的知识,并非OpenGL特有的,这里就不展开讲了,来看看加入变换矩阵后的vertex shader

precision mediump float;
attribute vec4 a_Position;

uniform vec2 u_Translate;
uniform float u_Scale;
uniform float u_Rotate;

void main() {
   mat4 translateMatrix = mat4(1.0, 0.0, 0.0, 0.0,
                              0.0, 1.0, 0.0, 0.0,
                              0.0, 0.0, 1.0, 0.0,
                              u_Translate.x, u_Translate.y, 0.0, 1.0);
   mat4 scaleMatrix = mat4(u_Scale, 0.0, 0.0, 0.0,
                        0.0, u_Scale, 0.0, 0.0,
                        0.0, 0.0, 1.0, 0.0,
                        0.0, 0.0, 0.0, 1.0);
   mat4 rotateMatrix = mat4(cos(u_Rotate), sin(u_Rotate), 0.0, 0.0,
                         -sin(u_Rotate), cos(u_Rotate), 0.0, 0.0,
                         0.0, 0.0, 1.0, 0.0,
                         0.0, 0.0, 0.0, 1.0);
    gl_Position = translateMatrix * rotateMatrix * scaleMatrix * a_Position;
}
复制代码

咱们向vertex shader中传递平移量u_Translate、缩放量u_Scale和旋转量u_Rotate(单位是弧度),前面演示过了平移,下面咱们利用这个vertex shader来演示缩放,将u_Scale设置为0.5,平移和旋转设置为0:

// 获取字段u_Offset在shader中的位置
// Get the location of translate in the shader
val uTranslateLocation = GLES20.glGetUniformLocation(programId, "u_Translate")

// 启动对应位置的参数
// Enable the parameter of the location
GLES20.glEnableVertexAttribArray(uTranslateLocation)

// 指定u_Offset所使用的顶点数据
// Specify the vertex data of translate
GLES20.glUniform2f(uTranslateLocation, 0f, 0f)

// 获取字段u_Offset在shader中的位置
// Get the location of u_Scale in the shader
val uScaleLocation = GLES20.glGetUniformLocation(programId, "u_Scale")

// 启动对应位置的参数
// Enable the parameter of the location
GLES20.glEnableVertexAttribArray(uScaleLocation)

// 指定u_Scale所使用的顶点数据
// Specify the vertex data of u_Scale
GLES20.glUniform1f(uScaleLocation, 0.5f)

// 获取字段u_Offset在shader中的位置
// Get the location of u_Rotate in the shader
val uRotateLocation = GLES20.glGetUniformLocation(programId, "u_Rotate")

// 启动对应位置的参数
// Enable the parameter of the location
GLES20.glEnableVertexAttribArray(uRotateLocation)

// 指定u_Rotate所使用的顶点数据
// Specify the vertex data of u_Rotate
GLES20.glUniform1f(uRotateLocation, Math.toRadians(0.0).toFloat())
复制代码

来看看效果:

咱们再来看看让它旋转90度,平移设为0,缩放设为1:

奇怪,旋转是旋转了,但为何感受形状变了呢?在第一篇文章中有提到过,咱们直接给gl_Position传递坐标的话,这时就至关因而传了设备标准化坐标,也就是xy的取值范围都是-1~1,而宽比长要小,一样的一个值在x轴上就显示小一些,好比(0, 0.5)这个点,旋转90度后变成(-0.5, 0)x轴上的0.5y轴上的0.5要短些,能够作些简单的换算,让它变得正常,咱们传入GLSurfaceView宽高比,来作换算:

precision mediump float;
attribute vec4 a_Position;

uniform vec2 u_Translate;
uniform float u_Scale;
uniform float u_Rotate;
uniform float u_Ratio;

void main() {
    vec4 p = a_Position;
    p.y = p.y / u_Ratio;
    mat4 translateMatrix = mat4(1.0, 0.0, 0.0, 0.0,
                              0.0, 1.0, 0.0, 0.0,
                              0.0, 0.0, 1.0, 0.0,
                              u_Translate.x, u_Translate.y, 0.0, 1.0);
    mat4 scaleMatrix = mat4(u_Scale, 0.0, 0.0, 0.0,
                        0.0, u_Scale, 0.0, 0.0,
                        0.0, 0.0, 1.0, 0.0,
                        0.0, 0.0, 0.0, 1.0);
    mat4 rotateMatrix = mat4(cos(u_Rotate), sin(u_Rotate), 0.0, 0.0,
                         -sin(u_Rotate), cos(u_Rotate), 0.0, 0.0,
                         0.0, 0.0, 1.0, 0.0,
                         0.0, 0.0, 0.0, 1.0);
    p = translateMatrix * rotateMatrix * scaleMatrix * p;
    p.y = p.y * u_Ratio;
    gl_Position = p;
}
复制代码
// 获取字段u_Ratio在shader中的位置
// Get the location of u_Rotate in the shader
val uRatioLocation = GLES20.glGetUniformLocation(programId, "u_Ratio")

// 启动对应位置的参数
// Enable the parameter of the location
GLES20.glEnableVertexAttribArray(uRatioLocation)

// 指定u_Ratio所使用的顶点数据
// Specify the vertex data of u_Ratio
GLES20.glUniform1f(uRatioLocation, glSurfaceViewWidth * 1.0f / glSurfaceViewHeight)
复制代码

如今再看看效果:

如今就正常了。

咱们来作一下组合,将平移设为(0.3, 0.3),缩放设为0.5,旋转设为45度,看看效果:

代码在我github的OpenGLES2.0SamplesForAndroid项目中,本文对应的是SampleVertexShader,项目连接:github.com/kenneycode/…

感谢阅读!

相关文章
相关标签/搜索