彩色图转灰度图

优质
小牛编辑
130浏览
2023-12-01

上一节课讲解了纹理贴图的知识点,本节课通过一个把彩色图处理为灰度图的案例进一步认识可编程片元着色器和逐片元的概念。

熊猫彩色图 灰度图

纹理贴图可以经过渲染管线处理后映射到三维空间中顶点坐标定义的位置,在这个过程中,执行方法texture2D()提取的像素值直接赋值给片元,存入帧缓存的颜色缓冲区中, 显示系统扫描颜色缓冲区中的像素值显示在屏幕上。渲染过线的片元着色器是可编程的,可以执行着色器语言编写的程序,也就是说可以对图片的纹素进行后期处理, 就像生活中你利用PS等软件去处理你拍的照片风格化,它的本质都是按照一定的算法处理图片,更改像素RGBA值。

亮度

灰度图颜色只有黑白两色,或者说灰度图颜色分量只有光亮度这一个分量,黑色相当于没有光照,白色相当于最大光照强度。那么光的亮度和RGB颜色模型的三原色有什么样的数学关系, 简单的理解,RGB分量越大,灰度图就越接近于白色,具体的计算公式如下,RGB的系数之和为1,这样可以保证计算的结果不会超出WebGL颜色分量默认的最大值1。

亮度 = 0.299 x R + 0.587 x G + 0.114 x B

纹理的加载处理处理过程和上一节课的一样,这里只列出片元着色器要执行的程序,片元着色器的计算语句要写在主函数main()里面。

void main() {
  //采集纹素
  vec4 texture = texture2D(u_Sampler,v_TexCoord);
  //计算RGB三个分量光能量之和,也就是亮度
  float luminance = 0.299*texture.r+0.587*texture.g+0.114*texture.b;
  //逐片元赋值,RGB相同均为亮度值,用黑白两色表达图片的明暗变化
  gl_FragColor = vec4(luminance,luminance,luminance,1.0);
}

执行vec4 texture = texture2D(u_Sampler,v_TexCoord);代码中WebGL API gl.texture2D(),片元着色器会从纹理缓冲区的纹理单元中获取像素值RGB,返回vec4类型向量数据,vec4的三个分量分别是RGB和A,jpg格式图片没有透明度分量, 程序默认补1表示不透明,通过运算符点(.)可以访问RGBA分量,float luminance = 0.299*texture.r+0.587*texture.g+0.114*texture.b;代码执行的是彩色图转灰度图的公式:亮度 = 0.299 x R + 0.587 x G + 0.114 x B, 通过代码gl_FragColor = vec4(luminance,luminance,luminance,1.0);把计算结果赋值给片元,片元的RGB分量相同都是亮度值,显示效果只有黑白两色。

其它后期处理

更改透明度

半透明

在实际的工程中,对像素值进行后期处理是很常见的操作,通常都是通过片元着色器程序控制片元着色器处理像素值的分量,你可以把gl_FragColor = vec4(luminance,luminance,luminance,1.0);代码vec4构造函数的最后一个分量1.0更改为0.5, 你会发现熊猫图片的颜色会与canvas画布的背景颜色进行融合,熊猫图片呈现出半透明状态。

拉深图片

拉伸

图片颜色值可以更改,显示尺寸也可以实现拉伸,本案例程序中图片的尺寸宽高比例是1,顶点坐标矩形区域宽高比也是1,显示效果正常。你可以把顶点数据的y值正值变大,负值变小, 刷新浏览器查看效果你会发现熊猫图片纵向拉伸。

/**
 * 四个顶点坐标数据data,z轴为零
 * 定义纹理贴图在WebGL坐标系中位置
 **/
var data=new Float32Array([
    -0.5, 0.5,//左上角——v0
    -0.5,-0.5,//左下角——v1
    0.5,  0.5,//右上角——v2
    0.5, -0.5 //右下角——v3
]);
/**
 * 四个顶点坐标数据data,z轴为零
 * 定义纹理贴图在WebGL坐标系中位置
 **/
var data=new Float32Array([
    -0.5, 0.7,//左上角——v0
    -0.5,-0.7,//左下角——v1
    0.5,  0.7,//右上角——v2
    0.5, -0.7 //右下角——v3
]);

旋转图片

拉伸

纹理贴图是映射在WebGL顶点位置坐标数据定义的矩形平面上,如果把矩形顶点位置和前面课程中立方体一样绕着x和y轴进行旋转,自然可以呈现出图片旋转的效果。

具体效果查看案例源码“1.图片映射的位置旋转变换.html”

/**uniform声明旋转矩阵变量mx、my**/
uniform mat4 mx;//绕x轴旋转矩阵
uniform mat4 my;//绕y轴旋转矩阵
void main() {
  //两个旋转矩阵、顶点齐次坐标连乘
  gl_Position = mx*my*a_Position;
  //纹理坐标插值计算
  v_TexCoord = a_TexCoord;
}
/**
 * 传入旋转矩阵数据
 ***/
var angle = Math.PI/6;//旋转角度
var sin = Math.sin(angle);
var cos = Math.cos(angle);
//旋转矩阵数据
var mxArr = new Float32Array([1,0,0,0,  0,cos,-sin,0,  0,sin,cos,0,  0,0,0,1]);
var myArr = new Float32Array([cos,0,-sin,0,  0,1,0,0,  sin,0,cos,0,  0,0,0,1]);
//类型数组传入矩阵
gl.uniformMatrix4fv(mx, false, mxArr);
gl.uniformMatrix4fv(my, false, myArr);