OpenGL ES着色器使用详解(二)

本文介绍了OpenGL ES着色器使用的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

1.着色器语言

着色器语言是一种高级图形编程语言,和C/C++语言很类似,但存在很大差别,比如,不支持double,byte

,short,不支持unin,enum,unsigned以及位运算等,但其加入了很多原生的数据类型,如向量,矩阵等。

数据类型可分为标量、向量、矩阵、采样器、结构体、数组等

向量

向量传递参数,如果只提供一个标量,这个值用于设置所有向量的值;如果输入是多个标量或者是矢量,从左到右设置矢量变量的参数,如果多个矢量作为参数,那么至少要有和变量一样多的分量

vec4 myVec4 = vec4(1.0); // myVec4 = {1.0, 1.0, 1.0, 1.0}
vec3 myVec3 = vec3(1.0, 0.0, 0.5); // myVec3 = {1.0, 0.0, 0.5}
vec3 temp = vec3(myVec3); // temp = myVec3
vec2 myVec2 = vec2(myVec3); // myVec2 = {myVec3.x, myVec3.y}
myVec4 = vec4(myVec2, temp); // myVec4 = {myVec2.x, myVec2.y, temp.x, temp.y}

矩阵

矩阵操作在OpenGL ES中的使用非常广泛,涉及到图形的平移缩放旋转等操作都是由矩阵来实现的.

向矩阵传递参数:

  • 提供的是一个标量,那么标量复制给与矩阵的主对角线
  • 一个矩阵能被多个向量赋值,如,mat2可以用两个vec2赋值
  • 一个的矩阵被多个标量赋值,按列赋值

向量和矩阵的分量

向量一般用来存储位置、颜色纹理坐标等包含不止一个的量,访问向量中某个分量的方法为:<向量名.分量名>

  • 将向量看做颜色对待,四个分量为r、g、b、a,分别代表红、绿、蓝、透明度
  • 将向量看做位置对待,四个分量为x、y、z、w,分别代表x轴、y轴、z轴、w
  • 将向量看做纹理坐标对待,四个分量为s、t、p、q,分别代表纹理坐标的不同分量

这三种不同的命名方案不能混合使用,除此之外还可以将向量当做数组看待,用下表来访问。

vec3 myVec3 = vec3(0.0, 1.0, 2.0); // myVec3 = {0.0, 1.0, 2.0}
float x = myVec3.x;
vec3 temp;
temp = myVec3.xyz; // temp = {0.0, 1.0, 2.0}
temp = myVec3.xxx; // temp = {0.0, 0.0, 0.0}
temp = myVec3.zyx; // temp = {2.0, 1.0, 0.0}
vec4 temp2 = myVec3.xxyz; // temp2 = {0.0, 0.0, 1.0, 2.0}

对矩阵的访问当成一个二维数组即可,矩阵可以认为是由多个向量组成的

mat4 myMat4 = mat4(1.0); // Initialize diagonal to 1.0 (identity)
vec4 col0 = myMat4[0]; // Get col0 vector out of the matrix
float m1_1 = myMat4[1][1]; // Get element at [1][1] in matrix
float m2_2 = myMat4[2].z; // Get element at [2][2] in matrix

采样器

采样器专门用于进行纹理采样的相关操作,一般情况下一个采样器变量代表了衣服纹理切贴图。

sampler2D/sampler3D/samplerCube

采样器变量不是在着色器中初始化的,一般是由主程序传递进来的。

数组

声明数组时指定数组大小,反之,访问数组时的下表必须是编译时常量,这样的话,编译器会自动创建适当大小的数组

类型转换

着色器语言没有自动提升的功能,也不能强制转换,只能用构造器完成类型转换,每中内建变量类型都有一组相关的构造器。

float f = 1; // error
int i = 0.0; // error
float f = 1.0 // ok
bool b = bool(f) // ok,非0当做true
float f = float(b) // ok,bool转为浮点数,true转为1.0,false转为0.0
int i = 0; //ok
bool b = bool(i) // ok,int转为bool

变量限定符

const:常量,编译时常量,其值不可变,可以提高运行效率

attribute:属性变量,仅仅用在顶点着色器,用该限定符修饰的变量用来接受从宿主程序传进渲染管线的变量。一般用于每个顶点都不相同的量,比如顶点位置,颜色,法线等

uniform:统一变量,一般用于对同一组顶点组成的一个物体所有顶点都相同的量,比如光源位置,转换矩阵,颜色,光照等

varying:变量被用来存储顶点着色器的输出和片元着色器的输入,每个顶点着色器把输出数据转变成一个或更多片元着色器的输入,在光栅化阶段就会插值生成一系列变量

varying变量的原理

在线段上进行混合插值

在三角形上进行混合插值

获取着色器变量

获取attribute类型变量。对于attribute限定符修饰的变量的值是由宿主程序传入渲染管线的,使用glGetAttribLocation函数获得着色器中某属性变量的引用

public static native int glGetAttribLocation(
  int program, // 创建的程序对象
  String name // 着色器中变量名
);

然后使用glVertexAttribPointer函数将数据传递到glGetAttribLocation返回的着色器变量引用所代表的变量中去

public static void glVertexAttribPointer(
  int indx, // 属性变量的引用
  int size, //每个顶点的数据个数,比如x、y、z就是3
  int type, // 数据类型,如GLES20.GL_FLOAT
  boolean normalized, // 是否规格化,只有使用整形数据才有意义
  int stride, // 跨距,一个数组存储多个属性才有意义,指的是两个点之间有多少个字节
  java.nio.Buffer ptr // 存放顶点数据缓冲
 )

获取uniform类型的变量。使用glGetUniformLocation函数获得着色器中某统一变量的引用

public static native int glGetUniformLocation(
  int program,
  String name
 );

然后使用glUniformXXX函数将数据传递到着色器中,比如glUniformMatrix4fv函数

public static native void glUniformMatrix4fv(
  int location, // 统一变量的引用
  int count, // 指明要更改的元素个数。如果变量不是数组,这个值应该设为1
  boolean transpose, // 是否要转置矩阵,并将它作为uniform变量的值。必须为false
  float[] value, // 传递给统一变量的数组元素
  int offset // 偏移,取0
 );

glUniformNf/glUniformNfv:将N个浮点数传入管线

glUniformNi/glUniformNiv:将N个整数传入管线

glUniformMatrixNfv:将N个整数传入管线,将N*N矩阵传入管线

内建变量

内建变量不需要声明即可使用,内建变量分为两种,输入与输出变量。

输入变量负责将渲染管线中固定功能部分生成的信息传递进着色器以供程序员使用,输出变量负责将着色器产生的信息传递给渲染管线中的固定功能。

顶点着色器

顶点着色器的内建变量主要是输出变量,即将着色器产生的值传递给渲染管线,因此在顶点着色器中要对这些内建变量赋值,包括gl_Position、gl_PointSize等。

  • gl_Position:在顶点着色器对获取到的定点原始数据进行平移缩放旋转等变换后,生成新的位置,新的顶点位置通过该变量传递给渲染管线的后续操作。
  • gl_PointSize:顶点着色器中可以计算一个点的大小,单位为像素,默认值为1,一般对点绘制方式有意义。

片元着色器

片元着色器中的内建输入变量,gl_FragCoord、gl_FrontFacing,并且还是只读的,是由渲染管线片元着色器之前阶段生成的。

  • gl_FragCoord:vec4类型数据,含有当前片元相对窗口位置的坐标。
  • gl_FrontFacing:bool类型的内建输入变量,该值表明当前正在处理的片元是否属于在光栅化阶段生成此片元对应图元的正面。点、线段没有正反面之分的图元。其生成的偏远都会被默认为是正面,三角形图元其正面取决于程序中队卷绕的设置及图元中顶点的具体卷绕情况。

片元着色器中的内建输出变量gl_FragColor、gl_FragData,在片元着色器中给这两个内建变量写入值。

  • gl_FragColo:vec4变量,用来传入由片元着色器计算出来的片元颜色值。

函数

和其他语言一样,差别在于参数可以指定用途,具体的有in,out,inout修饰符表明该参数是入参还是出参。

片元着色器浮点变量精度

片元着色器中的浮点类型数据必须制定精度,不指定精度可能引起编译错误。有三种精度类型:lowp、mediump、highp,一般使用mediump类型即可。如果在开发中同一个片元着色器中浮点类型变凉都是同一种精度类型,可以整个指定着色器中浮点类型默认精度。

precision <精度> <类型>
precision mediump float;

2.着色器程序

需要创建两个对象才能用着色器进行渲染:着色器对象和程序对象。

着色器源代码被编译成一个目标形式(类似obj文件),编译之后,着色器对象可以连接到一个程序对象,程序对象可以连接多个着色器对象。

获得连接后的着色器对象的过程:

  1. 创建一个顶点着色器和一个片元着色器:
  2. 将源代码连接到每个着色器对象
  3. 编译着色器对象
  4. 创建一个程序对象
  5. 将编译后的着色器对象连接到程序对象
  6. 连接程序对象

如果没有出错,就可以在后面使用这个程序了,如从程序获取某个着色器变量,接下来为其传递值等操作。

创建着色器对象

public static native int glCreateShader(
 int type // 着色器类型,GLES20.GL_VERTEX_SHADER或GLES20.GL_FRAGMENT_SHADER
);

连接源代码到着色器对象

public static native void glShaderSource(
  int shader,
  String string // 着色器源码
 );

编译着色器对象

public static native void glCompileShader(
  int shader
 );

创建程序对象

mProgram = GLES20.glCreateProgram();

将编译后的着色器对象连接到程序对象

public static native void glAttachShader(
  int program,
  int shader
 );

连接程序对象

public static native void glLinkProgram(
  int program
 );

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • OpenGL Shader实例分析(7)雪花飘落效果

    研究了一个雪花飘落效果,感觉挺不错的,分享给大家,效果如下: 代码如下: Shader "shadertoy/Flakes" { // https://www.shadertoy.com/view/4d2Xzc Properties{ iMouse ("Mouse Pos", Vector) = (100,100,0,0) iChannel0("iChannel0", 2D) = "white" {} iChannelReso

  • OpenGL实现Bezier曲线的方法示例

    Bezier曲线的形状是通过一组多边折线(特征多边形)的各顶点唯一地定义出来的.在这组顶点中: (1)只有第一个顶点和最后一个顶点在曲线上: (2)其余的顶点则用于定义曲线的导数.阶次和形状: (3)第一条边和最后一条边则表示了曲线在两端点处的切线方向. // BezierCurve.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h"</div></div></li><li><div class=

  • OpenGL关于glStencilFuncSeparate()和glStencilFunc()函数的区别讲解

    glStencilFunc()函数是OpenGL提供的对模板缓冲区进行控制的命令,这是OpenGL2.0之前使用的函数,其函数原型为 void glStencilFunc(GLenum func, GLint ref, GLuint mask). func指定比较函数,它指定了测试通过的条件,其取值可以是:(为方便表示,参考值为refValue, 缓冲区值bufferValue) GL_NEVER                        总是不通过测试 GL_ALWAYS        

  • Android利用OpenGLES绘制天空盒实例教程

    前言 天空盒这个效果最早是在腾讯的实景地图里看到的,当时觉得很牛逼,但是没有想过自己去实现以下.最近这段时间对opengl很有兴趣,顺便就搞了这个天空盒,话不多说,先上效果. 天空盒的原理就是在三维空间中放置一个正方体,然后将我们的相机放置在正方体内,当我们的视点转动,相机跟着转动.我们就可以看到相应的景色的变换了,天空盒本质上是一个立方体. OpenGL 关于什么是OpenGL,什么是OpenGLES就不细说了,不了解的就自行百度吧,我们主要是关注代码.整个项目采用了Kotlin + Ndk的

  • OpenGL中的glutInitDisplayMode()函数的理解

    OpenGL中的glutInitDisplayMode()函数的作用主要是在创建窗口的时候,指定其显示模式的类型. 函数原型为:void glutInitDisplayMode(unsigned int mode); mode参数是一个GLUT库里预定义的可能的布尔组合.你使用mode去指定颜色模式,数量和缓冲区类型. 其中大部分情况下使用的参数为: GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL 颜色模式:GLUT_RGBA表示颜色模式,

  • SDL2和OpenGL使用踩坑笔记经验分享

    SDL + OpenGL使用笔记 LFTK 是一个嵌入式GUI,为了开发方便,需要提供PC运行环境.我选择了SDL2+OpenGL+nanovg来实现底层的渲染,让LFTK可以运行在各个平台上.GLFW+OpenGL也是一个不错的选择,但是GLFW没有Android和iOS的移植,而且没有提供原生输入法的支持.LFTK虽然最初是为嵌入式系统而生,但也有一个小目标:可以用于开发嵌入式系统,也可以开发PC软件和移动APP,所以最后选择了SDL2+OpenGL+nanovg.在使用SDL2+OpenG

  • OpenGL Shader实例分析(1)Wave效果

    这篇文章主要分析一个Shader,从而感受shader的魅力,并学习相关shader的函数的用法. 先看Shader运行的效果: 下面是代码: Shader "shadertoy/Waves" { //see https://www.shadertoy.com/view/4dsGzH CGINCLUDE #include "UnityCG.cginc" #pragma target 3.0 struct vertOut { float4 pos:SV_POSITIO

  • OpenGL ES透视投影实现方法(四)

    在之前的学习中,我们知道了一个顶点要想显示到屏幕上,它的x.y.z分量都要在[-1,1]之间,我们回顾一下渲染管线的图元装配阶段,它实际上做了以下几件事:剪裁坐标.透视分割.视口变换.图元装配的输入是顶点着色器的输出,抓哟是物体坐标gl_Position,之后到光栅化阶段. 图元装配 剪裁坐标 当顶点着色器写入一个值到gl_Position时,这个点要求必须在剪裁空间中,即它的x.y.z坐标必须在[-w,w]之间,任何这个范围之外的点都是不可见的. 这里需要注意以下,对于attribute类型的

  • android使用OPENGL ES绘制圆柱体

    本文实例为大家分享了android使用OPENGL ES绘制圆柱体的具体代码,供大家参考,具体内容如下 效果图: 编写jiem.java *指定屏幕所要显示的假面,并对见.界面进行相关设置     *为Activity设置恢复处理,当Acitvity恢复设置时显示界面同样应该恢复     *当Activity暂停设置时,显示界面同样应该暂停 package com.scout.eeeeeee; import android.app.Activity; import android.os.Bund

  • opengl实现任意两点间画圆柱体

    本文实例为大家分享了opengl实现任意两点间画圆柱体的具体代码,供大家参考,具体内容如下 1.问题提出 两点间画线简单: glBegin(GL_LINES);  //注意是LINES不是LINE,这个错误一定要注意. glVertexf(x1, y1, z1); glVertexf(x2, y2, z2); glEnd(); 画线函数不会影响opengl的矩阵堆栈. 但是很多时候线条效果会比较差,比如我要做一个骨骼动画,关节点间的骨头用线条太难看,即使使用glLineWidth设置线宽,视觉效

随机推荐