GLSL快速入门,用Shader实现彩虹特效

导语

本文将先简要介绍一下Shader和GLSL,然后用一个简单的fragment shader的例子来实现一个彩虹的特效

Shader简介

Shader又称着色器,是一段跑在GPU上的程序,在旧的图形渲染管线中,图形开发者对显卡渲染流程的控制很有限,随着着色器的程序的出现,开发者可以在着色器程序中执行几何变换、光照模型、纹理贴图等复杂的计算,极大地提高了灵活性和效率,因而可以完成一些更复杂的渲染任务,实现更加炫酷的特效。不同的显卡厂商提供的图形接口有不同的着色器语言(Shader Language)语法,比如微软Direct3D的HLSL,Nvidia的Cg,以及OpenGL的GLSL等,本文讨论的就是GLSL的简单语法入门和应用。

OpenGL中的着色器程序主要包含两部分,一部分是Vertex Shader(顶点着色器),一部分是Fragment Shader(片元着色器,或称像素着色器),顶点着色器的主要任务包括对传到GPU中的顶点数据进行处理,比如进行适当的矩阵变换以及进行光照的计算、生成变换纹理坐标等等,顶点着色器针对每一个顶点都会执行一次。片元着色器则是对最终会绘制到屏幕上的每个像素点进行着色,计算相应的颜色值,片元着色器针对屏幕上每一个位置的像素都会执行一次,即每段Fragment Shader针对屏幕上每个像素点都会运行一次,且每个像素点之间的处理相互并行,互不干扰,理解这一点对我们后面用shader来绘图十分重要。正因为如此在高分辨率的渲染任务中只有图形显卡才能支持这种大规模的逐像素并行处理。

GLSL基础语法

OpenGL(包括OpenGL ES,移动平台专用)经过很多版本的迭代,GLSL也发生了很大改变,比如从2.0到3.0的接口就有了很大的丰富,其中比较重要的一个就是现代OpenGL(Core Profile)中如果设置了非Compatibility即非向后兼容的模式,则一些旧的标记废弃的API则无法在新版本的OpenGL环境中生效。

下面我们先来看一段OpenGL 2.0版本的Fragement Shader(OpenGL 2.0版本对应的GLSL版本是110),了解一下最基本的一些语法概念。

#version 110
attribute vec3 aPosition ;
attribute vec4 aTextureColor;
uniform mat4 uMVPMatrix;
varying vec4 vTextureColor ;

void main(){
   gl_Position= uMVPMatrix * vec4(aPosition, 1);
   vTextureColor = aTextureColor; 
}
attribute

首先第一行,声明了GLSL的版本,其次是两个attribute变量,attribute是顶点着色器的输入变量,只能在vertex shader中使用,来表示一些顶点的数据,如:顶点坐标,法线,纹理坐标,顶点颜色等,可以通过以下方法来从程序中将值传给attribute变量

GLint glGetAttribLocation(GLuint program, const GLchar *name); //根据变量名name获取该attribute变量在shader程序中的位置
void glVertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); //给该attribute变量代表的顶点赋值四个浮点数
void glVertexAttrib4fv(GLuint index, const GLfloat *v); //给该attribute变量代表的顶点赋值含有四个浮点数的数组
void glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer); //根据顶点数组和索引传值

vec3就是指三维向量,在shader中我们用三维向量来代表一个基本的RGB颜色,vec3三个分量可以通过xyz,也可以通过rgb来分别访问,如果是vec4的话,第四个分量一般就代表的是alpha值,如下的操作在shader中都是合法的

vec3 a;
vec4 = vec4( a.xyz, 1.0 );
vec3 b = a.rgb;

需要注意的是一般这里的数字尽量用带小字母的浮点数,否则的话在某些GPU上可能会出现问题,参考 The Book of Shaders中的建议

uniform

第三行的uniform变量,传入了一个MVP变换矩阵,其实是包含Model模型变换、View视图变换、Projection投影变换三个阶段的变换的总称,这里合并成了一个矩阵,关于OpenGL几种变换关系大家还不清楚的可以移步OpenGL的官方示例OpenGL-Tutotial矩阵,里面有形象的图片例子。uniform是CPU往GPU传值的重要途径,因为GPU渲染屏幕中每个像素的过程都是互相并行且互不干扰的,所以传到shader里里面的uniform变量的值都是常量,只读且不能被shader程序修改

varing

第五行varing变量则是指顶点着色器用来传给片元着色器的变量,在这段代码里就是将CPU传过来的纹理坐标从顶点着色器传递到片元着色器中

内置变量

gl_Position就是顶点着色器的一个内置变量,代表该顶点的位置,又如一些片元着色器的内置变量,gl_FragColorgl_FragCoord分别代表该像素点的颜色和位置,除了内置变量以外,GLSL还有丰富的内置数学函数,诸如smoothstepclamp等,后续的文章会详细介绍

新版本GLSL的差异

值的注意的是,GLSL 130(也就是OpenGL 3.0对应的版本以后)版本以后,attribute变量和varying变量被in和out声明所取代,在330版本以后,还可以通过location布局限定符来指定变量的位置,如下图330版本的Shader

#version 330
layout(location = 0) in vec3 aVertexPosition;
layout(location = 1) in vec4 aVertexColor;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;

out vec4 vColor;

void main(void) {
     gl_Position = uProjectionMatrix* uModelViewMatrix * vec4(aVertexPosition, 1.0);
     vColor = aVertexColor;
}

这里的in还是指代aVertexPosition和aVertexColor是从CPU传递过来的值,out表明该值会传到片元着色器,作用与varying变量相同

绘制彩虹

好了,有了上面基本的一些概念,我们来探讨一下如何进行彩虹的绘制,需要注意到一个片元着色器的内置变量,gl_FragCoord,代表你即将要上色的顶点的位置,最终的原理也很简单,实际上就是绘制几个不同半径的半圆,然后根据顶点的位置,如果在几个半圆内的弧形中的话,我们就给他指定颜色

#version 110
#ifdef GL_ES
precision highp float;
#endif
    
//将屏幕上的点的位置除以屏幕分辨率归一化,0.0~1.0之间
vec2 st = gl_FragCoord.xy/u_resolution;

//高中数学,三个不同半径的半圆方程
float y1 = sqrt(0.25 - (st.x-0.5) * (st.x - 0.5));
float y2 = sqrt(0.16 - (st.x-0.5) * (st.x - 0.5));
float y3 = sqrt(0.09 - (st.x-0.5) * (st.x - 0.5));
    
// 如果点位位于某两个圆中间,则指定颜色形成色带
if ((st.y >= y2 && st.y <= y1) || (st.y <= y1 && st.x >0.0 && st.x < 0.1) || (st.y <= y1 && st.x > 0.9)) {
    gl_FragColor = vec4(1.0,0.0,0.0,1.0);
} else if ((st.y > y3 && st.y < y2) || (st.y < y2 && st.x >= 0.1 && st.x < 0.2) ||  ) {
    gl_FragColor = vec4(1.0,1.0,0.0,1.0);
}

好了,按照上面的方式,一个简单的彩虹(颜色不全)shader就形成啦,不是特别复杂,不过可以提炼出很多基础的东西,比如shader本质就是通过shape Function等数学函数形成曲线或者映射,然后通过给屏幕上的每个像素计算颜色值来形成效果,更多酷炫的shader效果和创意大家可以参考ShaderToy这个网站,以及 The Book of Shaders中很多基础概念的讲解,例子也很多,关键是多动手练习,多写一些shader,就能加深理解啦

更多OpenGL和图形学相关的知识也可以关注我的个人公众号,专注图形图像,游戏引擎,音视频,移动开发,大厂内推,不定时更新

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值