片段着色器:texture.fsh
precision
表示精度 lowp低、mediump中、highp高
很容易想到,精度越↑,效果越↑,但着色器速度↓
in vec2 vTexCoord;
表示接受顶点的输入的vTexCoord变量uniform
统一变量,在着色器执行期间它的值是不变的sampler2D
类型:2D纹理
#version 300 es
precision mediump float;
out vec4 outColor;
in vec2 vTexCoord;
uniform sampler2D sTexture;
void main(){
outColor = texture(sTexture, vTexCoord);
}
片段的out标识的变量就是输出的色值,sTexture承载纹理,vTexCoord承载坐标
通过texture函数获取值像素的色值,作为输出量。就相当于图片拓印到了"纸"上
着色器颜色效果处理
灰度图片特效
texture函数返回一个四维向量,是一个颜色的rgba四个分量值
我们只要轻轻的将outColor
的rgb都改为g值即可
---->[gray.fsh]----
#version 300 es
precision mediump float;
out vec4 outColor;
in vec2 vTexCoord;
uniform sampler2D sTexture;
void main(){
vec4 color= texture(sTexture, vTexCoord);
outColor = vec4(color.g, color.g, color.g, 1.0);
}
纯黑白图片特效
还有一种灰度计算方式35911(左一):g = r * 0.3 + g * 0.59 + b * 0.11;
变灰之后,根据灰值可以获取纯黑白色的图片,即像素值大于阈值为黑,小于为白
通过阈值的更改可以控制颜色的通量
,阈值↓,通量↓。阈值=0,不通,白色
通过阈值的控制,颜色不太复杂的图就可以变成线稿(左三)。
#version 300 es
precision mediump float;
out vec4 outColor;
in vec2 vTexCoord;
uniform sampler2D sTexture;
const float threshold = 0.3;//阈值
void main(){
vec4 color= texture(sTexture, vTexCoord);
float r = color.r;
float g = color.g;
float b = color.b;
g = r * 0.3 + g * 0.59 + b * 0.11;
g= g <= threshold ? 0.0 : 1.0;
outColor = vec4(g, g, g, 1.0);
}
向着色器中传参控制
threshold如果只能写死在着色器代码里,未免有些鸡肋。
如何将它放入程序中进行动态控制呢? 比如下面的效果:
---->[black_white.fsh]----
#version 300 es
precision mediump float;
out vec4 outColor;
in vec2 vTexCoord;
uniform sampler2D sTexture;
uniform float uThreshold;//uniform入参
void main(){
vec4 color= texture(sTexture, vTexCoord);
float r = color.r;
float g = color.g;
float b = color.b;
g = r * 0.3 + g * 0.59 + b * 0.11;
g= g <= uThreshold ? 0.0 : 1.0;
outColor = vec4(g, g, g, 1.0);
}
其实和前面的顶点入参差不多,在绘制的代码里找到uThreshold句柄再赋值即可
之后就是setThreshold方法设置值,外部通过一个Slider进行控制
//寻找句柄
uThreshold= GLES30.glGetUniformLocation(program, "uThreshold");
public void draw() {
//...
GLES30.glUseProgram(program);
GLES30.glUniform1f(uThreshold, threshold); //赋值
负片、怀旧、冷调
负片
是将rgb色值用1去减,获取相对的颜色怀旧
是将图片变成偏黄的暖调,冷调
中只是将r和b的色值进行对调,就能达到相反的效果
---->[负片]----
r = 1.0 - color.r;
g = 1.0 - color.g;
b = 1.0 - color.b;
---->[怀旧]----
r = 0.393* r + 0.769 * g + 0.189* b;
g = 0.349 * r + 0.686 * g + 0.168 * b;
b = 0.272 * r + 0.534 * g + 0.131 * b;
---->[冷调]----
b = 0.393* r + 0.769 * g + 0.189* b;
g = 0.349 * r + 0.686 * g + 0.168 * b;
r = 0.272 * r + 0.534 * g + 0.131 * b;
其他的代码都是类似的,核心是色值的算法。
当然你也可以通过变量来控制这些系数,就可以成为简单的图片编辑器。
流年效果
主要是对b值进行处理,arg
越大,越发蓝
#version 300 es
precision mediump float;
out vec4 outColor;
in vec2 vTexCoord;
uniform sampler2D sTexture;
void main(){
float arg = 3.0;
vec4 color= texture(sTexture, vTexCoord);
float r = color.r;
float g = color.g;
float b = color.b;
b = sqrt(b)*arg;
if (b>1.0) b = 1.0;
outColor = vec4(r, g, b, 1.0);
}
着色器坐标效果处理
除了色值,还有一个非常重要的可用数据就是贴图坐标
可以通过坐标值进行一些位置上的处理,比如对称,旋转,缩放,分屏等
图片x,y反向
现在不要把它对称一张图片,而是一个个像素拼组成的对象
现在vTexCoord记录着这些像素的位置,改动vTexCoord就可以改变像素的位置
宽为1.0,如果仅是拿1.0- pos.x
,就相当于右边的像素跑到左边了(下图2),其他同理
---->[x反]---
#version 300 es
precision mediump float;
out vec4 outColor;
in vec2 vTexCoord;
uniform sampler2D sTexture;
void main(){
vec2 pos = vTexCoord.xy;
pos.x= 1.0- pos.x;
outColor = texture(sTexture, pos);
}
---->[y反]---
#version 300 es
precision mediump float;
out vec4 outColor;
in vec2 vTexCoord;
uniform sampler2D sTexture;
void main(){
vec2 pos = vTexCoord.xy;
pos.y= 1.0- pos.y;
outColor = texture(sTexture, pos);
}
---->[x,y反]---
#version 300 es
precision mediump float;
out vec4 outColor;
in vec2 vTexCoord;
uniform sampler2D sTexture;
void main(){
vec2 pos = vTexCoord.xy;
pos.x= 1.0- pos.x;
pos.y= 1.0- pos.y;
outColor = texture(sTexture, pos);
}
分屏
分屏也就是根据像素位置判断,去修改读取纹理的位置坐标,从而影响渲染
比如二分,当y大于0.5是,读取的位置是pos.y - 0.5,相当于还是渲染上面部分
你也可以调整0.5这个参数,达到不等分分屏。
#version 300 es
precision mediump float;
out vec4 outColor;
in vec2 vTexCoord;
uniform sampler2D sTexture;
void main(){
vec2 pos = vTexCoord.xy;
if (pos.y<= 0.5) {
pos.y = pos.y ;
}else{
pos.y = pos.y - 0.5;
}
outColor = texture(sTexture, pos);
}
也可以左右分镜:
#version 300 es
precision mediump float;
out vec4 outColor;
in vec2 vTexCoord;
uniform sampler2D sTexture;
void main(){
vec2 pos = vTexCoord;
if (pos.x > 0.5) {
pos.x = 1.0 - pos.x;
}
outColor = texture(sTexture, pos);
}
三分
#version 300 es
precision mediump float;
out vec4 outColor;
in vec2 vTexCoord;
uniform sampler2D sTexture;
void main(){
vec2 pos = vTexCoord.xy;
if (pos.y<= 1.0/3.0) {
pos.y = pos.y * 1.0;
}else if(pos.y<= 2.0/3.0){
pos.y = (pos.y - 1.0/3.0) * 1.0;
}else{
pos.y = (pos.y - 2.0/3.0) * 1.0;
}
outColor = texture(sTexture, pos);
}
四分
#version 300 es
precision mediump float;
out vec4 outColor;
in vec2 vTexCoord;
uniform sampler2D sTexture;
void main(){
vec2 pos = vTexCoord.xy;
if(pos.x <= 0.5){
pos.x = pos.x ;
}else{
pos.x = pos.x - 0.5;
}
if (pos.y<= 0.5) {
pos.y = pos.y ;
}else{
pos.y = pos.y - 0.5;
}
outColor = texture(sTexture, pos);
}
也许你觉得四分屏时填满多好啊(图中),
如果每个分镜可以处理不同效果
那就更棒了(图右)
#version 300 es
precision mediump float;
out vec4 outColor;
in vec2 vTexCoord;
uniform sampler2D sTexture;
void main(){
vec2 pos = vTexCoord.xy;
if(pos.x <= 0.5){
pos.x = pos.x * 2.0;
}else{
pos.x = (pos.x - 0.5) * 2.0;
}
if (pos.y<= 0.5) {
pos.y = pos.y * 2.0;
}else{
pos.y = (pos.y - 0.5) * 2.0;
}
outColor = texture(sTexture, pos);
}
四分特效其实也很简单,可就是分成四块去判断,分别处理罢了核心的算法都是上面介绍过的
。是不是感觉自己不知不觉就会写些复杂的对象了?
#version 300 es
precision mediump float;
out vec4 outColor;
in vec2 vTexCoord;
uniform sampler2D sTexture;
void main(){
vec2 pos = vTexCoord.xy;
vec4 result;
if(pos.x <= 0.5 && pos.y<= 0.5){ //左上
pos.x = pos.x * 2.0;
pos.y = pos.y * 2.0;
vec4 color = texture(sTexture, pos);
result = vec4(color.g, color.g, color.g, 1.0);
}else if (pos.x >= 0.5 && pos.y<= 0.5){//右上
pos.x = (pos.x - 0.5) * 2.0;
pos.y = (pos.y - 0.5) * 2.0;
vec4 color= texture(sTexture, pos);
float r = color.r;
float g = color.g;
float b = color.b;
g = r * 0.3 + g * 0.59 + b * 0.11;
g= g <= 0.4 ? 0.0 : 1.0;
result = vec4(g, g, g, 1.0);
}else if (pos.y> 0.5 && pos.x < 0.5) {//左下
pos.y = pos.y * 2.0;
pos.x = pos.x * 2.0;
vec4 color= texture(sTexture, pos);
float r = color.r;
float g = color.g;
float b = color.b;
r = 0.393* r + 0.769 * g + 0.189* b;
g = 0.349 * r + 0.686 * g + 0.168 * b;
b = 0.272 * r + 0.534 * g + 0.131 * b;
result = vec4(r, g, b, 1.0);
}else if (pos.y> 0.5 && pos.x > 0.5){//右下
pos.y = (pos.y - 0.5) * 2.0;
pos.x = (pos.x - 0.5) * 2.0;
vec4 color= texture(sTexture, pos);
float r = color.r;
float g = color.g;
float b = color.b;
b = 0.393* r + 0.769 * g + 0.189* b;
g = 0.349 * r + 0.686 * g + 0.168 * b;
r = 0.272 * r + 0.534 * g + 0.131 * b;
result = vec4(r, g, b, 1.0);
}
outColor = result;
}
局部效果
可以控制位置量后,我们就可以做些有意思的事,比如,局部特效
可以通过区域的判断,来指定部分区域进行操作,比如(椭)圆
原来很简单,通过到指定点的距离判断,就可以截获区域
#version 300 es
precision mediump float;
out vec4 outColor;
in vec2 vTexCoord;
uniform sampler2D sTexture;
void main(){
float centerX =0.4;
float centerY =0.8;
float raduius =0.3;
vec2 pos = vTexCoord.xy;
vec4 color= texture(sTexture, vTexCoord);
float r = color.r;
float g = color.g;
float b = color.b;
if((pos.x-centerX)*(pos.x-centerX)+(pos.y-centerY)*(pos.y-centerY)<raduius*raduius){//表示在圆的区域内
outColor = vec4(g, g, g, 1.0);
}else{
outColor = vec4(r, g, b, 1.0);
}
}
注意,由于宽高不同,而最大值都是1.0,所以量纲是不同的
区域是椭圆也不用惊讶,解决方法很简单,传入一个宽高比校正即可下面的rate可以提成变量,由java代码传入,java的Bitmap可以获取宽高
如果有闲情逸致,其他三个量也能提出来,就是一个灰图的探照灯
如果还有闲情逸致,可以定义多个特效效果,通过变量控制一下
就能变成特效探照灯
,照到哪里,哪里就特效。
#version 300 es
precision mediump float;
out vec4 outColor;
in vec2 vTexCoord;
uniform sampler2D sTexture;
void main(){
float rate= 1000.0/1369.0;
float centerX =0.5;
float centerY =0.5/rate;
float raduius =0.25;
vec2 pos;
pos.x = vTexCoord.x;
pos.y= vTexCoord.y/rate;
vec4 color= texture(sTexture, vTexCoord);
float r = color.r;
float g = color.g;
float b = color.b;
if ((pos.x-centerX)*(pos.x-centerX)+(pos.y-centerY)*(pos.y-centerY)<raduius*raduius){ //表示在圆的区域内
outColor = vec4(g, g, g, 1.0);
} else {
outColor = vec4(r, g, b, 1.0);
}
}
光照效果
根据上面的效果,实现一下局部的光照效果
#version 300 es
precision mediump float;
out vec4 outColor;
in vec2 vTexCoord;
uniform sampler2D sTexture;
void main(){
float rate= 1000.0/1369.0;
float centerX =0.5;
float centerY =0.5/rate;
float radius =0.6;
float strength = 150.0/255.0;//#设置光照强度
vec2 pos;
pos.x = vTexCoord.x;
pos.y= vTexCoord.y/rate;
vec4 color= texture(sTexture, vTexCoord);
float r = color.r;
float g = color.g;
float b = color.b;
float distance = sqrt((pos.x-centerX)*(pos.x-centerX)+(pos.y-centerY)*(pos.y-centerY));
if (distance<radius){ //表示在圆的区域内
//按照距离大小计算增强的光照值
float result = strength*( 1.0 - distance / radius );
r = r+result;
g = g+result;
b = b+result;
outColor = vec4(r, g, b, 1.0);
} else {
outColor = vec4(r, g, b, 1.0);
}
}
矩形马赛克
马赛克需要指定一行的个数,以及多少个占一块
比如100块,5*5,也就是一行20个小块。这些参数都可以提出去玩
#version 300 es
precision mediump float;
out vec4 outColor;
in vec2 vTexCoord;
uniform sampler2D sTexture;
void main(){
float rate= 1000.0/1369.0;
float cellX= 5.0;
float cellY= 5.0;
float rowCount=100.0;
vec2 pos = vTexCoord;
pos.x = pos.x*rowCount;
pos.y = pos.y*rowCount/rate;
pos = vec2(floor(pos.x/cellX)*cellX/rowCount, floor(pos.y/cellY)*cellY/(rowCount/rate))+ 0.5/rowCount*vec2(cellX, cellY);
outColor = texture(sTexture, pos);
}
局部特效以及会了,马赛克也ok了,局部马赛克还远吗?
局部马赛克
#version 300 es
precision mediump float;
out vec4 outColor;
in vec2 vTexCoord;
uniform sampler2D sTexture;
void main(){
float rate= 1000.0/1369.0;
float centerX =0.4;
float centerY =0.3/rate;
float radius =0.15;
float cellX= 1.0;
float cellY=1.0;
float rowCount=100.0;
vec2 pos;
pos.x = vTexCoord.x;
pos.y= vTexCoord.y/rate;
float distance = sqrt((pos.x-centerX)*(pos.x-centerX)+(pos.y-centerY)*(pos.y-centerY));
if (distance<radius){ //表示在圆的区域内
vec2 pos = vTexCoord;
pos.x = pos.x*rowCount;
pos.y = pos.y*rowCount/rate;
pos = vec2(floor(pos.x/cellX)*cellX/rowCount, floor(pos.y/cellY)*cellY/(rowCount/rate))+ 0.5/rowCount*vec2(cellX, cellY);
outColor = texture(sTexture, pos);
} else {
outColor = texture(sTexture, vTexCoord);
}
}
图片点阵
和矩形马赛克类似,将图片分成若干个块,当在cell半径之内, 绘制UVMosaic
点的像素值,否则,绘制白色
#version 300 es
precision mediump float;
out vec4 outColor;
in vec2 vTexCoord;
uniform sampler2D sTexture;
void main(){
float rate= 1000.0/1369.0;
float cellX= 3.0;
float cellY=3.0;
float rowCount=100.0;
vec2 sizeFmt=vec2(rowCount, rowCount/rate);
vec2 sizeMsk=vec2(cellX, cellY/rate);
vec2 posFmt = vec2(vTexCoord.x*sizeFmt.x, vTexCoord.y*sizeFmt.y);
vec2 posMsk = vec2(floor(posFmt.x/sizeMsk.x)*sizeMsk.x, floor(posFmt.y/sizeMsk.y)*sizeMsk.y)+ 0.5*sizeMsk;
float del = length(posMsk - posFmt);
vec2 UVMosaic = vec2(posMsk.x/sizeFmt.x, posMsk.y/sizeFmt.y);
vec4 result;
if (del< cellX/2.0)
result = texture(sTexture, UVMosaic);
else
result = vec4(1.0,1.0,1.0,0.0);
outColor = result;
}
基于此很容易实现圆形的马赛克
#version 300 es
//英雄所见...
void main(){
//英雄所见...
else
result = texture(sTexture, vTexCoord);
outColor = result;
}
图片加点
这个特效让我赚了一顿午饭钱,需求是将图片变灰加点,本来毫无头绪
聪明的我灵机一动,这不就是小版的马赛克,载把马赛克区域涂黑吗?
#version 300 es
precision mediump float;
out vec4 outColor;
in vec2 vTexCoord;
uniform sampler2D sTexture;
void main(){
float rate= 1000.0/1369.0;
float cellX= 1.0;
float cellY=1.0;
float rowCount=100.0;
float space =4.0;
vec2 sizeFmt=vec2(rowCount, rowCount/rate);
vec2 sizeMsk=vec2(cellX, cellY/rate);
vec2 posFmt = vec2(vTexCoord.x*sizeFmt.x, vTexCoord.y*sizeFmt.y);
vec2 posMsk = vec2(floor(posFmt.x/sizeMsk.x)*sizeMsk.x, floor(posFmt.y/sizeMsk.y)*sizeMsk.y)+ 0.5*sizeMsk;
float del = length(posMsk - posFmt);
vec2 UVMosaic = vec2(posMsk.x/sizeFmt.x, posMsk.y/sizeFmt.y);
vec4 result;
if (del< cellX/space)
result=vec4(0.2,0.2,0.2,0.1);
else
result = texture(sTexture, vTexCoord);
outColor = vec4(result.g,result.g,result.g,1.0);
}
灵魂出窍
核心是周期性的获取一个逐渐放大,逐渐透明的图片和原图进行叠合
#version 300 es
precision highp float;
in vec2 vTexCoord;
out vec4 outColor;
uniform sampler2D sTexture;
//传递进来的时间
uniform float uProgress;
void main () {
float t = 0.7; //周期
float maxAlpha = 0.4;//第二图最大透明度
float maxScale = 1.8;//第二图放大最大比率
//进度
float progress = mod(uProgress, t) / t; // 0~1
//当前的透明度
float alpha = maxAlpha * (1.0 - progress);
//当前的放大比例
float scale = 1.0 + (maxScale - 1.0) * progress;
//根据放大比例获取新的图层纹理坐标
vec2 weakPos = vec2(0.5 + (vTexCoord.x - 0.5) / scale, 0.5 + (vTexCoord.y - 0.5) / scale);
//新图层纹理坐标对应的纹理像素值
vec4 weakMask = texture(sTexture, weakPos);
vec4 mask = texture(sTexture, vTexCoord);
//纹理像素值的混合公式,获得混合后的实际颜色
outColor = mask * (1.0 - alpha) + weakMask * alpha;
}