教你如何用代码调戏GPU

05-nexus-6a_01

前言

随着近年来人工智能,3D视觉的频繁出现和应用,这方面需要的数据计算量相比以前的应用要大得多,而且需要跟GPU打交道。对于前端开发来说,也需要逐步补充这方面的知识。本文会教大家认识并使用代码调戏GPU,做一些画图方面的尝试,体验GPU处理数据的魅力。

一.为什么要使用GPU

在回答这个问题之前,我们可以先了解下CPU。假设CPU是一个大的管道,每个任务都是通过这个管道来完成。如果有些任务特别大,那么它就需要花费更多的时间和精力去处理,也就是说这些任务是串行处理的,即一个接一个处理。也可以把这些管道称为线程。

1

假设要让一个分辨率为1024×1080的屏幕来完成一次动画渲染的任务,也就是要每帧处理1024×1080个像素,假设每秒运行30帧,那么就需要进行1024x1080x30次计算。这对于CPU来讲,计算量太大了,需要换一种方式来解决。

2

假设把一堆CPU管道排列成一个管道墙,每个像素是一个小球,那么1024×1080个小球可以在这个管道墙无阻塞顺畅通过完成。这种处理的方式其实就是并行处理的方案,也是GPU(Graphic Processor Unit)处理数据的方式,而且GPU芯片可通过硬件加速调用复杂的数学函数公式,无须通过软件调用。

3

二.如何使用代码驱动GPU

对于GPU,驱动它运行显示的是OpenGL着色语言,简称为GLSL。它们是一系列的指令,这些指令会对屏幕的每个像素同时进行操作。程序会根据输入的信息,输出颜色信息,编译之后会高速运行。

举个例子来说明

例子1:

 #ifdef GL_ES precision mediump float; //选择中等精度,会影响渲染速度,lowp为低精度,highp为高精度 #endif

void main() { 

  gl_FragColor = vec4(1.0,0.0,0.0,1.0); //预设的全局变量,决定输出颜色 gl_FragColor 

}

运行结果:

4

可以看出,GLSL语言有一个main函数,会在最后返回颜色值,有点像C语言,最终的像素颜色取决于这个全局变量默认输出值 (vec4) gl_FragColor。这里的参数最小为0,最大为1,分别对应0和255,所以这里的vec4(1,0,0.0,0.0,1.0)也就对应于rgba(255,0,0,1)。

简单介绍一下GLSL会用到的数据类型

数据类型 解释 数据类型 解释
vec4 四分量浮点向量 vec3 三分量浮点向量
vec2 二分量浮点向量 float 单精度浮点型
int 整型 bool 布尔型

除此之外GLSL还有一些内置的变量,这些内置的变量类型为uniform,且为只读。

 


uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

我们可以尝试用内置变量对上面例子做一下改造

例子2:


#ifdef GL_ES
precision mediump float; //选择中等精度,会影响渲染速度,lowp为低精度,highp为高精度
#endif

uniform float u_time;

void main() {
 gl_FragColor = vec4(abs(sin(u_time)),0.0,0.0,1.0);
 //使用时间变量来改变颜色 
}

运行结果:

5

可以看出,加入时间变量后颜色会随之变化。

同样的,GLSL也有一个默认输入值 (vec4)gl_FragCoord,gl_FragCoord存储了活动进程正在处理的像素,可以尝试一下

例子3:


#ifdef GL_ES
precision mediump float; //选择中等精度,会影响渲染速度,lowp为低精度,highp为高精度
#endif

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

void main() {
 vec2 st = gl_FragCoord.xy/u_resolution;
 gl_FragColor = vec4(st.x,st.y,0.0,1.0);
 //使用输入变量来改变颜色 
}

运行结果:

6

这里其实就是将gl_FragCoord.xy 除以 u_resolution,对坐标进行格式化为0.0到1.0,并将这些值放到颜色输出参数,也就形成了渐变的颜色

以上介绍了关于颜色方面的尝试,下面介绍一下怎么画线

例子4:


#ifdef GL_ES
precision mediump float; //选择中等精度,会影响渲染速度,lowp为低精度,highp为高精度
#endif

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

//用0.0 - 1.0 之间的值在Y轴上进行画线
float plot(vec2 st, float pct){
  return  smoothstep( pct-0.02, pct, st.y) -
          smoothstep( pct, pct+0.02, st.y);
}

void main() {
  vec2 st = gl_FragCoord.xy/u_resolution;
  float y = cos(st.x);
  vec3 color = vec3(y);

  // 画线
  float pct = plot(st,y);
  color = (1.0-pct)*color;

  gl_FragColor = vec4(color,1.0);
}

运行结果:

7

这里的原理是,使用了plot函数将屏幕上的像素进行插值逼近,最终形成一条线,也就是说,通过这个函数可以将数学公式进行可视化,可以对第17行的代码进行修改展示其他数学公式。

三.运行GLSL

光有代码还不够,如何运行调戏GLSL才是更重要的,这里有2个方法供参考

A.使用sublime Text 插件(适用于mac)

步骤1.安装glslviewer

brew update
brew upgrade
brew install glslviewer

步骤2.在sublime Text  安装 glslviewer插件

步骤3.把文件保存为frag格式,将文件用sublime Text打开,使用ctrl+b运行glslviewer插件进行查看

 

B.使用glslcanvas(无平台限制)

步骤1.安装glslcanvas

npm install glslCanvas

步骤2.在html运行代码文件即可预览

<canvas class="glslCanvas" width="500" height="500" data-fragment-url="1.frag"></canvas>
<script src="./GlslCanvas.min.js" type="text/javascript"></script>

总结

GLSL还可以用来生成随机图案,图像滤镜处理等。本文只是初浅地介绍了如何使用GLSL来画图,希望可以敲砖引玉,帮助大家更好的理解图像处理背后的原理。文章如有错误的地方,欢迎指正,谢谢。

参考资料

https://packagecontrol.io/packages/glslViewer

https://learnopengl-cn.readthedocs.io/zh/latest/01%20Getting%20started/05%20Shaders/

https://webglfundamentals.org/webgl/lessons/zh_cn/webgl-shaders-and-glsl.html

发表评论