赞
踩
目录
5. 使用原始着色器(rawShaderMaterial)实现旗帜飘动效果
着色器(Shaders )是一种使用GLSL(OpenGL Shading Language)编写并在GPU上运行的程序。它们被用于定位几何体的每个顶点,并为该几何体的每个可见像素着色。使用“像素Pixel”来描述其实并不准确,因为渲染的每个点不一定与屏幕上的每个像素相匹配,因此我们更倾向于使用术语“片元fragment”。
之后我们会向着色器发送大量数据,如顶点坐标、网格变换、摄像机及其视野范围的信息、颜色、纹理、灯光、雾等参数。然后,GPU会按照着色器的指示处理所有的这些数据,接着几何体便出现在渲染中。
Shaders 也是一系列的指令,但是这些指令会对屏幕上的每个像素同时下达。也就是说,你的代码必须根据像素在屏幕上的不同位置执行不同的操作。就像活字印刷,你的程序就像一个 function(函数),输入位置信息,输出颜色信息,当它编译完之后会以相当快的速度运行。
下面一起来看“Hello world!”示例:
- #ifdef GL_ES
- precision mediump float;
- #endif
-
- uniform float u_time;
-
- void main() {
- gl_FragColor = vec4(0.533,0.650,1.000,1.000);
- }
实现效果:
尽管这几行简单的代码看起来不像有很多内容,我们还是可以据此推测出一些知识点:
shader 语言 有一个 main
函数,会在最后返回颜色值。这点和 C 语言很像。
最终的像素颜色取决于预设的全局变量 gl_FragColor
。
这个类 C 语言有内建的变量(像gl_FragColor
),函数和数据类型。在本例中我们刚刚介绍了vec4
(四分量浮点向量)。之后我们会见到更多的类型,像 vec3
(三分量浮点向量)和 vec2
(二分量浮点向量),还有非常著名的:float
(单精度浮点型), int
(整型) 和 bool
(布尔型)。
如果我们仔细观察 vec4
类型,可以推测这四个变元分别响应红,绿,蓝和透明度通道。同时我们也可以看到这些变量是规范化的,意思是它们的值是从0到1的。之后我们会学习如何规范化变量,使得在变量间map(映射)数值更加容易。
另一个可以从本例看出来的很重要的类 C 语言特征是,预处理程序的宏指令。宏指令是预编译的一部分。有了宏才可以 #define
(定义)全局变量和进行一些基础的条件运算(通过使用 #ifdef
和 #endif
)。所有的宏都以 #
开头。预编译会在编译前一刻发生,把所有的命令复制到 #defines
里,检查#ifdef
条件句是否已被定义, #ifndef
条件句是否没有被定义。在我们刚刚的“hello world!”的例子中,我们在第2行检查了 GL_ES
是否被定义,这个通常用在移动端或浏览器的编译中。
float
类型在 shaders 中非常重要,所以精度非常重要。更低的精度会有更快的渲染速度,但是会以质量为代价。你可以选择每一个浮点值的精度。在第一行(precision mediump float;
)我们就是设定了所有的浮点值都是中等精度。但我们也可以选择把这个值设为“低”(precision lowp float;
)或者“高”(precision highp float;
)。
OpenGL着色语言(OpenGL Shading Language)是用来在OpenGL中着色编程的语言,也即开发人员写的短小的自定义程序,他们是在图形卡的GPU (Graphic Processor Unit图形处理单元)上执行的,代替了固定的渲染管线的一部分,使渲染管线中不同层次具有可编程性。比如:视图转换、投影转换等。GLSL(GL Shading Language)的着色器代码分成2个部分:Vertex Shader(顶点着色器)和Fragment(片断着色器),有时还会有Geometry Shader(几何着色器)。负责运行顶点着色的是顶点着色器。它可以得到当前OpenGL 中的状态,GLSL内置变量进行传递。GLSL其使用C语言作为基础高阶着色语言,避免了使用汇编语言或硬件规格语言的复杂性。
GLSL的变量命名方式与C语言类似。变量的名称可以使用字母,数字以及下划线,但变量名不能以数字开头,还有变量名不能以gl_作为前缀,这个是GLSL保留的前缀,用于GLSL的内部变量。当然还有一些GLSL保留的名称是不能够作为变量的名称的。
WebGL的着色器代码分为顶点着色器代码和片元着色器代码两部分,顶点着色器代码会在GPU的顶点着色器单元执行,片元着色器代码会在GPU的片元着色器单元执行,在WebGL渲染管线流程中,或者说GPU的渲染流程中,顶点着色器代码先执行处理顶点,得到一系列片元,然后再执行片元着色器代码处理片元。
顶点着色器(Vertex Shader)的作用是定位几何体的顶点。其思想是发送顶点位置、网格变换(如定位position、旋转rotation和缩放scale)、摄影机信息(如定位position、旋转rotation和视野fov)。然后,GPU将按照顶点着色器中的指示处理所有这些信息,以便将顶点投影到2D空间,该空间将成为我们的渲染render,也就是我们的画布canvas。
片元着色器的作用是为几何体的每个可见片元(像素)进行着色。
我们会创建片元着色器,可以通过使用uniform将数据(像是颜色)和着色器发送至GPU,之后GPU就会按照指令对每个片元进行着色。
参考:
1)顶点着色器收到系统传递给它的模型数据。
2)顶点着色器把模型数据处理成我们后续需要的数据进行输出(这些数据还包括纹理的UV坐标以及其他需要传递给片元着色器的数据)。
3)系统对顶点着色器输出的顶点数据进行插值,并将插值结果传递给片元着色器。
4)片元着色器根据这些插值结果计算最后屏幕上的像素颜色。
着色器材质(ShaderMaterial)是一个用GLSL编写的小程序 ,在GPU上运行。它能够提供 materials 之外的效果,也可以将许多对象组合成单个Geometry或BufferGeometry以提高性能。
每个着色器材质都可以指定两种不同类型的shaders,他们是顶点着色器和片元着色器(Vertex shaders and fragment shaders)。
shader中有三种类型的变量: uniforms, attributes, 和 varyings
关键字(变量类型) | 数据传递 | 声明变量 |
---|---|---|
attribute | javascript——>顶点着色器 | 声明顶点数据变量 |
uniform | javascript——>顶点、片元着色器 | 声明非顶点数据变量 |
varying | 顶点着色器——>片元着色器 | 声明需要插值计算的顶点变量 |
注意:在shader 内部,uniforms和attributes就像常量;你只能使用JavaScript代码通过缓冲区来修改它们的值。
炫彩蛋示例:
- var geom = new THREE.SphereGeometry(10, 30, 20);
- var mate = new THREE.ShaderMaterial({
- vertexShader: `
- varying vec3 vNormal;
- void main() {
- //将attributes的normal通过varying赋值给了向量vNormal
- vNormal = normal;
- //projectionMatrix是投影变换矩阵 modelViewMatrix是相机坐标系的变换矩阵 最后我们将y值乘以1.4得到了一个形如鸡蛋的几何体
- gl_Position = projectionMatrix * modelViewMatrix * vec4( position.x, position.y * 1.4, position.z, 1.0 );
- }
- `,
- fragmentShader: `
- //片元着色器同样需要定义varying vec3 vNormal;
- varying vec3 vNormal;
- void main() {
- //vNormal是一个已经归一化的三维向量
- float pr = (vNormal.x + 1.0) / 2.0; //pr红色通道值范围为0~1
- float pg = (vNormal.y + 1.0) / 2.0; //pg绿色通道值范围为0~1
- float pb = (vNormal.z + 1.0) / 2.0; //pb蓝色通道值范围为0~1
- gl_FragColor=vec4(pr, pg, pb, 1.0); //最后设置顶点颜色,点与点之间会自动插值
- }
- `
- })
- var mesh = new THREE.Mesh(geom, mate);
- scene.add(mesh)
实现效果:
- // 创建着色器材质
- const shaderMaterial = new THREE.ShaderMaterial({
- //顶点着色器
- vertexShader: `
- void main(){
- gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ) ;
- }
- `,
- //片元着色器
- fragmentShader: `
- void main(){
- gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);//rgba
- }
- `,
- });
- // 创建平面
- const floor = new THREE.Mesh(
- new THREE.PlaneBufferGeometry(1, 1, 64, 64),
- shaderMaterial
- );
-
- console.log(floor);
- scene.add(floor);
实现效果:
此外,在进行项目构建时,由于涉及较多着色器编写,建议对着色器文件单独进行编写,例如:
并在js文件中通过import导入使用
- // 顶点着色器
- import basicVertexShader from "../shader/basic/vertex.glsl";
- // 片元着色器
- import basicFragmentShader from "../shader/basic/fragment.glsl";
在shaderMaterial中调用着色器:
- // 创建着色器材质
- const shaderMaterial = new THREE.ShaderMaterial({
- vertexShader: basicVertexShader,
- fragmentShader: basicFragmentShader,
- });
在插件商店中搜索Shader languages support for VS Code插件,可以对glsl等着色器语言进行语法支持。
fragment.glsl:
- precision lowp float;精度
- // highp -2^16 - 2^16
- // mediump -2^10 - 2^10
- // lowp -2^8 - 2^8
- varying vec2 vUv;
- varying float vElevation;
- //导入采样纹理
- uniform sampler2D uTexture;
-
-
- void main(){
- // gl_FragColor = vec4(vUv, 0.0, 1.0);//由uv渲染颜色(0,1)
- // float height = vElevation + 0.05 * 10.0;
- // gl_FragColor = vec4(1.0*height,0.0, 0.0, 1.0);
-
-
- float height = vElevation + 0.05 * 20.0;//0~2
- // 根据UV,取出对应的颜色
- //根据uv进行采样
- vec4 textureColor = texture2D(uTexture,vUv);
- textureColor.rgb*=height;
- gl_FragColor = textureColor;
- }
vertex.glsl:
- //精度
- // highp -2^16 - 2^16
- // mediump -2^10 - 2^10
- // lowp -2^8 - 2^8
- precision lowp float;
- attribute vec3 position;
- attribute vec2 uv;
-
-
- uniform mat4 modelMatrix;
- uniform mat4 viewMatrix;
- uniform mat4 projectionMatrix;
-
- // 获取时间
- uniform float uTime;
-
- //传到片元着色器的
- varying vec2 vUv;
-
- //高度
- varying float vElevation;
-
-
- void main(){
- vUv = uv;
- vec4 modelPosition = modelMatrix * vec4( position, 1.0 );
- // modelPosition.x += 1.0;
- // modelPosition.z += 1.0;
-
- // modelPosition.z += modelPosition.x;
- //使用sin函数实现波浪效果
- modelPosition.z = sin((modelPosition.x+uTime) * 10.0)*0.05 ;
- modelPosition.z += sin((modelPosition.y+uTime) * 10.0)*0.05 ;
- //把z的值传过去
- vElevation = modelPosition.z;
-
- gl_Position = projectionMatrix * viewMatrix * modelPosition ;
- }
main.js:
- import * as THREE from "three";
-
- import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
-
-
- // 顶点着色器
- import basicVertexShader from "../shader/raw/vertex.glsl";
- // 片元着色器
- import basicFragmentShader from "../shader/raw/fragment.glsl";
-
- // 目标:认识shader
- // 初始化场景
- const scene = new THREE.Scene();
-
- // 创建透视相机
- const camera = new THREE.PerspectiveCamera(
- 90,
- window.innerHeight / window.innerHeight,
- 0.1,
- 1000
- );
- // 设置相机位置
- // object3d具有position,属性是1个3维的向量
- camera.position.set(0, 0, 2);
- // 更新摄像头
- camera.aspect = window.innerWidth / window.innerHeight;
- // 更新摄像机的投影矩阵
- camera.updateProjectionMatrix();
- scene.add(camera);
-
- // 加入辅助轴,帮助我们查看3维坐标轴
- const axesHelper = new THREE.AxesHelper(5);
- scene.add(axesHelper);
-
- // 加载纹理
-
- // 创建纹理加载器对象
- const textureLoader = new THREE.TextureLoader();
- const texture = textureLoader.load("./texture/ca.jpeg");
- // 创建原始着色器材质
- const rawShaderMaterial = new THREE.RawShaderMaterial({
- vertexShader: basicVertexShader,
- fragmentShader: basicFragmentShader,
- // wireframe: true,
- side: THREE.DoubleSide,
- //设置传输的数据
- uniforms: {
- uTime: {
- value: 0,
- },
- uTexture: {
- value: texture,
- },
- },
- });
-
- // 创建平面
- const floor = new THREE.Mesh(
- new THREE.PlaneBufferGeometry(1, 1, 64, 64),
- rawShaderMaterial
- );
-
- console.log(floor);
- scene.add(floor);
-
- // 初始化渲染器
- const renderer = new THREE.WebGLRenderer({ alpha: true });
-
-
- // 设置渲染尺寸大小
- renderer.setSize(window.innerWidth, window.innerHeight);
-
- // 监听屏幕大小改变的变化,设置渲染的尺寸
- window.addEventListener("resize", () => {
- // console.log("resize");
- // 更新摄像头
- camera.aspect = window.innerWidth / window.innerHeight;
- // 更新摄像机的投影矩阵
- camera.updateProjectionMatrix();
-
- // 更新渲染器
- renderer.setSize(window.innerWidth, window.innerHeight);
- // 设置渲染器的像素比例
- renderer.setPixelRatio(window.devicePixelRatio);
- });
-
- // 将渲染器添加到body
- document.body.appendChild(renderer.domElement);
-
- // 初始化控制器
- const controls = new OrbitControls(camera, renderer.domElement);
- // 设置控制器阻尼
- controls.enableDamping = true;
- // 设置自动旋转
- // controls.autoRotate = true;
-
- const clock = new THREE.Clock();
- function animate(t) {
- const elapsedTime = clock.getElapsedTime();
- // console.log(elapsedTime);
- rawShaderMaterial.uniforms.uTime.value = elapsedTime;
- requestAnimationFrame(animate);
- // 使用渲染器渲染相机看这个场景的内容渲染出来
- renderer.render(scene, camera);
- }
-
- animate();
实现效果:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。