👨⚕️ 主页: gis分享者
👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅!
👨⚕️ 收录于专栏:threejs gis工程师
文章目录
- 一、🍀前言
- 1.1 ☘️THREE.ShaderMaterial
- 1.1.1 ☘️注意事项
- 1.1.2 ☘️构造函数
- 1.1.3 ☘️属性
- 1.1.4 ☘️方法
- 二、🍀使用ShaderMaterial自定义着色器材质
- 1. ☘️实现思路
- 2. ☘️代码样例
一、🍀前言
本文详细介绍如何基于threejs在三维场景中使用ShaderMaterial自定义着色器材质,亲测可用。希望能帮助到您。一起学习,加油!加油!
1.1 ☘️THREE.ShaderMaterial
THREE.ShaderMaterial使用自定义shader渲染的材质。 shader是一个用GLSL编写的小程序 ,在GPU上运行。
1.1.1 ☘️注意事项
- ShaderMaterial 只有使用 WebGLRenderer 才可以绘制正常, 因为 vertexShader 和
fragmentShader 属性中GLSL代码必须使用WebGL来编译并运行在GPU中。 - 从 THREE r72开始,不再支持在ShaderMaterial中直接分配属性。 必须使用
BufferGeometry实例,使用BufferAttribute实例来定义自定义属性。 - 从 THREE r77开始,WebGLRenderTarget 或 WebGLCubeRenderTarget
实例不再被用作uniforms。 必须使用它们的texture 属性。 - 内置attributes和uniforms与代码一起传递到shaders。
如果您不希望WebGLProgram向shader代码添加任何内容,则可以使用RawShaderMaterial而不是此类。 - 您可以使用指令#pragma unroll_loop_start,#pragma unroll_loop_end
以便通过shader预处理器在GLSL中展开for循环。 该指令必须放在循环的正上方。循环格式必须与定义的标准相对应。 - 循环必须标准化normalized。
- 循环变量必须是i。
- 对于给定的迭代,值 UNROLLED_LOOP_INDEX 将替换为 i 的显式值,并且可以在预处理器语句中使用。
#pragma unroll_loop_start
for ( int i = 0; i < 10; i ++ ) {// ...}
#pragma unroll_loop_end
代码示例
const material = new THREE.ShaderMaterial( {uniforms: {time: { value: 1.0 },resolution: { value: new THREE.Vector2() }},vertexShader: document.getElementById( 'vertexShader' ).textContent,fragmentShader: document.getElementById( 'fragmentShader' ).textContent
} );
1.1.2 ☘️构造函数
ShaderMaterial( parameters : Object )
parameters - (可选)用于定义材质外观的对象,具有一个或多个属性。 材质的任何属性都可以从此处传入(包括从Material继承的任何属性)。
1.1.3 ☘️属性
共有属性请参见其基类Material。
.clipping : Boolean
定义此材质是否支持剪裁; 如果渲染器传递clippingPlanes uniform,则为true。默认值为false。
.defaultAttributeValues : Object
当渲染的几何体不包含这些属性但材质包含这些属性时,这些默认值将传递给shaders。这可以避免在缓冲区数据丢失时出错。
this.defaultAttributeValues = {'color': [ 1, 1, 1 ],'uv': [ 0, 0 ],'uv2': [ 0, 0 ]
};
.defines : Object
使用 #define 指令在GLSL代码为顶点着色器和片段着色器定义自定义常量;每个键/值对产生一行定义语句:
defines: {FOO: 15,BAR: true
}
这将在GLSL代码中产生如下定义语句:
#define FOO 15
#define BAR true
.extensions : Object
一个有如下属性的对象:
this.extensions = {derivatives: false, // set to use derivativesfragDepth: false, // set to use fragment depth valuesdrawBuffers: false, // set to use draw buffersshaderTextureLOD: false // set to use shader texture LOD
};
.fog : Boolean
定义材质颜色是否受全局雾设置的影响; 如果将fog uniforms传递给shader,则为true。默认值为false。
.fragmentShader : String
片元着色器的GLSL代码。这是shader程序的实际代码。在上面的例子中, vertexShader 和 fragmentShader 代码是从DOM(HTML文档)中获取的; 它也可以作为一个字符串直接传递或者通过AJAX加载。
.glslVersion : String
定义自定义着色器代码的 GLSL 版本。仅与 WebGL 2 相关,以便定义是否指定 GLSL 3.0。有效值为 THREE.GLSL1 或 THREE.GLSL3。默认为空。
.index0AttributeName : String
如果设置,则调用gl.bindAttribLocation 将通用顶点索引绑定到属性变量。默认值未定义。
.isShaderMaterial : Boolean
只读标志,用于检查给定对象是否属于 ShaderMaterial 类型。
.lights : Boolean
材质是否受到光照的影响。默认值为 false。如果传递与光照相关的uniform数据到这个材质,则为true。默认是false。
.linewidth : Float
控制线框宽度。默认值为1。
由于OpenGL Core Profile与大多数平台上WebGL渲染器的限制,无论如何设置该值,线宽始终为1。
.flatShading : Boolean
定义材质是否使用平面着色进行渲染。默认值为false。
.uniforms : Object
如下形式的对象:
{ "uniform1": { value: 1.0 }, "uniform2": { value: 2 } }
指定要传递给shader代码的uniforms;键为uniform的名称,值(value)是如下形式:
{ value: 1.0 }
这里 value 是uniform的值。名称必须匹配 uniform 的name,和GLSL代码中的定义一样。 注意,uniforms逐帧被刷新,所以更新uniform值将立即更新GLSL代码中的相应值。
.uniformsNeedUpdate : Boolean
可用于在 Object3D.onBeforeRender() 中更改制服时强制进行制服更新。默认为假。
.vertexColors : Boolean
定义是否使用顶点着色。默认为假。
.vertexShader : String
顶点着色器的GLSL代码。这是shader程序的实际代码。 在上面的例子中,vertexShader 和 fragmentShader 代码是从DOM(HTML文档)中获取的; 它也可以作为一个字符串直接传递或者通过AJAX加载。
.wireframe : Boolean
将几何体渲染为线框(通过GL_LINES而不是GL_TRIANGLES)。默认值为false(即渲染为平面多边形)。
.wireframeLinewidth : Float
控制线框宽度。默认值为1。
由于OpenGL Core Profile与大多数平台上WebGL渲染器的限制,无论如何设置该值,线宽始终为1。
1.1.4 ☘️方法
共有方法请参见其基类Material。
.clone () : ShaderMaterial this : ShaderMaterial
创建该材质的一个浅拷贝。需要注意的是,vertexShader和fragmentShader使用引用拷贝; attributes的定义也是如此; 这意味着,克隆的材质将共享相同的编译WebGLProgram; 但是,uniforms 是 值拷贝,这样对不同的材质我们可以有不同的uniforms变量。
二、🍀使用ShaderMaterial自定义着色器材质
1. ☘️实现思路
首先创建6个面着色器fragment-shader-1到fragment-shader-6。
- 1、初始化renderer渲染器。
- 2、初始化Scene三维场景scene。
- 3、初始化camera相机,定义相机位置 camera.position.set,设置相机方向camera.lookAt。
- 4、创建THREE.AmbientLight环境光源ambientLight,设置环境光ambientLight颜色,scene场景加入环境光源ambientLight。创建THREE.SpotLight聚光灯光源spotLight,设置聚光灯光源位置和投影,scene场景加入spotLight。
- 5、加载几何模型:定义createMaterial方法用于创建ShaderMaterial自定义着色器材质,创建立方体集合对象cubeGeometry,调用createMaterial方法创建6个ShaderMaterial自定义着色器材质对象从meshMaterial1到meshMaterial6,传入创建的6个ShaderMaterial自定义着色器材质对象,创建MeshFaceMaterial几何体面材质对象material。传入cubeGeometry和material创建立方体网格对象cube,scene场景加入cube。定义render方法,实现立方体对象cube的旋转动画。具体代码参考下面代码样例。
- 6、加入stats监控器,监控帧数信息。
2. ☘️代码样例
<!DOCTYPE html>
<html>
<head><title>学习threejs,使用ShaderMaterial自定义着色器材质</title><script type="text/javascript" src="../libs/three.js"></script><script type="text/javascript" src="../libs/stats.js"></script><script type="text/javascript" src="../libs/dat.gui.js"></script><style>body {/* set margin to 0 and overflow to hidden, to go fullscreen */margin: 0;overflow: hidden;}</style>
</head>
<body><script id="vertex-shader" type="x-shader/x-vertex">uniform float time;varying vec2 vUv;void main(){vec3 posChanged = position;posChanged.x = posChanged.x*(abs(sin(time*1.0)));posChanged.y = posChanged.y*(abs(cos(time*1.0)));posChanged.z = posChanged.z*(abs(sin(time*1.0)));//gl_Position = projectionMatrix * modelViewMatrix * vec4(position*(abs(sin(time)/2.0)+0.5),1.0);gl_Position = projectionMatrix * modelViewMatrix * vec4(posChanged,1.0);}</script><script id="fragment-shader-1" type="x-shader/x-fragment">precision highp float;uniform float time;uniform float alpha;uniform vec2 resolution;varying vec2 vUv;void main2(void){vec2 position = vUv;float red = 1.0;float green = 0.25 + sin(time) * 0.25;float blue = 0.0;vec3 rgb = vec3(red, green, blue);vec4 color = vec4(rgb, alpha);gl_FragColor = color;}#define PI 3.14159#define TWO_PI (PI*2.0)#define N 68.5void main(void){vec2 center = (gl_FragCoord.xy);center.x=-10.12*sin(time/200.0);center.y=-10.12*cos(time/200.0);vec2 v = (gl_FragCoord.xy - resolution/20.0) / min(resolution.y,resolution.x) * 15.0;v.x=v.x-10.0;v.y=v.y-200.0;float col = 0.0;for(float i = 0.0; i < N; i++){float a = i * (TWO_PI/N) * 61.95;col += cos(TWO_PI*(v.y * cos(a) + v.x * sin(a) + sin(time*0.004)*100.0 ));}col /= 5.0;gl_FragColor = vec4(col*1.0, -col*1.0,-col*4.0, 1.0);}</script><script id="fragment-shader-2" type="x-shader/x-fragment">uniform float time;uniform vec2 resolution;#define CGFloat float#define M_PI 3.14159265359vec3 hsvtorgb(float h, float s, float v){float c = v * s;h = mod((h * 6.0), 6.0);float x = c * (1.0 - abs(mod(h, 2.0) - 1.0));vec3 color;if (0.0 <= h && h < 1.0){color = vec3(c, x, 0.0);}else if (1.0 <= h && h < 2.0){color = vec3(x, c, 0.0);}else if (2.0 <= h && h < 3.0){color = vec3(0.0, c, x);}else if (3.0 <= h && h < 4.0){color = vec3(0.0, x, c);}else if (4.0 <= h && h < 5.0){color = vec3(x, 0.0, c);}else if (5.0 <= h && h < 6.0){color = vec3(c, 0.0, x);}else{color = vec3(0.0);}color += v - c;return color;}void main(void){vec2 position = (gl_FragCoord.xy - 0.5 * resolution) / resolution.y;float x = position.x;float y = position.y;CGFloat a = atan(x, y);CGFloat d = sqrt(x*x+y*y);CGFloat d0 = 0.5*(sin(d-time)+1.5)*d;CGFloat d1 = 5.0;CGFloat u = mod(a*d1+sin(d*10.0+time), M_PI*2.0)/M_PI*0.5 - 0.5;CGFloat v = mod(pow(d0*4.0, 0.75),1.0) - 0.5;CGFloat dd = sqrt(u*u+v*v);CGFloat aa = atan(u, v);CGFloat uu = mod(aa*3.0+3.0*cos(dd*30.0-time), M_PI*2.0)/M_PI*0.5 - 0.5;// CGFloat vv = mod(dd*4.0,1.0) - 0.5;CGFloat d2 = sqrt(uu*uu+v*v)*1.5;gl_FragColor = vec4( hsvtorgb(dd+time*0.5/d1, sin(dd*time), d2), 1.0 );}</script><script id="fragment-shader-3" type="x-shader/x-fragment">uniform vec2 resolution;uniform float time;vec2 rand(vec2 pos){return fract( 0.00005 * (pow(pos+2.0, pos.yx + 1.0) * 22222.0));}vec2 rand2(vec2 pos){return rand(rand(pos));}float softnoise(vec2 pos, float scale){vec2 smplpos = pos * scale;float c0 = rand2((floor(smplpos) + vec2(0.0, 0.0)) / scale).x;float c1 = rand2((floor(smplpos) + vec2(1.0, 0.0)) / scale).x;float c2 = rand2((floor(smplpos) + vec2(0.0, 1.0)) / scale).x;float c3 = rand2((floor(smplpos) + vec2(1.0, 1.0)) / scale).x;vec2 a = fract(smplpos);return mix(mix(c0, c1, smoothstep(0.0, 1.0, a.x)),mix(c2, c3, smoothstep(0.0, 1.0, a.x)),smoothstep(0.0, 1.0, a.y));}void main(void){vec2 pos = gl_FragCoord.xy / resolution.y;pos.x += time * 0.1;float color = 0.0;float s = 1.0;for(int i = 0; i < 8; i++){color += softnoise(pos+vec2(i)*0.02, s * 4.0) / s / 2.0;s *= 2.0;}gl_FragColor = vec4(color);}</script><script id="fragment-shader-4" type="x-shader/x-fragment">uniform float time;uniform vec2 resolution;vec2 rand(vec2 pos){returnfract((pow(pos+2.0,pos.yx+2.0)*555555.0));}vec2 rand2(vec2 pos){return rand(rand(pos));}float softnoise(vec2 pos, float scale) {vec2 smplpos = pos * scale;float c0 = rand2((floor(smplpos) + vec2(0.0, 0.0)) / scale).x;float c1 = rand2((floor(smplpos) + vec2(1.0, 0.0)) / scale).x;float c2 = rand2((floor(smplpos) + vec2(0.0, 1.0)) / scale).x;float c3 = rand2((floor(smplpos) + vec2(1.0, 1.0)) / scale).x;vec2 a = fract(smplpos);return mix(mix(c0, c1, smoothstep(0.0, 1.0, a.x)),mix(c2, c3, smoothstep(0.0, 1.0, a.x)),smoothstep(0.0, 1.0, a.x));}void main( void ) {vec2 pos = gl_FragCoord.xy / resolution.y - time * 0.4;float color = 0.0;float s = 1.0;for (int i = 0; i < 6; ++i) {color += softnoise(pos + vec2(0.01 * float(i)), s * 4.0) / s / 2.0;s *= 2.0;}gl_FragColor = vec4(color,mix(color,cos(color),sin(color)),color,1);}</script><script id="fragment-shader-5" type="x-shader/x-fragment">uniform float time;uniform vec2 resolution;// tie nd die by Snoep Games.void main( void ) {vec3 color = vec3(1.0, 0., 0.);vec2 pos = (( 1.4 * gl_FragCoord.xy - resolution.xy) / resolution.xx)*1.5;float r=sqrt(pos.x*pos.x+pos.y*pos.y)/15.0;float size1=2.0*cos(time/60.0);float size2=2.5*sin(time/12.1);float rot1=13.00; //82.0+16.0*sin(time/4.0);float rot2=-50.00; //82.0+16.0*sin(time/8.0);float t=sin(time);float a = (60.0)*sin(rot1*atan(pos.x-size1*pos.y/r,pos.y+size1*pos.x/r)+time);a += 200.0*acos(pos.x*2.0+cos(time/2.0))+asin(pos.y*5.0+sin(time/2.0));a=a*(r/50.0);a=200.0*sin(a*5.0)*(r/30.0);if(a>5.0) a=a/200.0;if(a<0.5) a=a*22.5;gl_FragColor = vec4( cos(a/20.0),a*cos(a/200.0),sin(a/8.0), 1.0 );}</script><script id="fragment-shader-6" type="x-shader/x-fragment">uniform float time;uniform vec2 resolution;void main( void ){vec2 uPos = ( gl_FragCoord.xy / resolution.xy );//normalize wrt y axis//suPos -= vec2((resolution.x/resolution.y)/2.0, 0.0);//shift origin to centeruPos.x -= 1.0;uPos.y -= 0.5;vec3 color = vec3(0.0);float vertColor = 2.0;for( float i = 0.0; i < 15.0; ++i ){float t = time * (0.9);uPos.y += sin( uPos.x*i + t+i/2.0 ) * 0.1;float fTemp = abs(1.0 / uPos.y / 100.0);vertColor += fTemp;color += vec3( fTemp*(10.0-i)/10.0, fTemp*i/10.0, pow(fTemp,1.5)*1.5 );}vec4 color_final = vec4(color, 1.0);gl_FragColor = color_final;}</script><div id="Stats-output">
</div>
<!-- Div which will hold the Output -->
<div id="WebGL-output">
</div><!-- Js 代码块-->
<script type="text/javascript">// 初始化function init() {var stats = initStats();// 创建三维场景scenevar scene = new THREE.Scene();// 创建相机var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);// 创建渲染器并设置大小var renderer = new THREE.WebGLRenderer();renderer.setClearColor(new THREE.Color(0x000000, 1.0));renderer.setSize(window.innerWidth, window.innerHeight);renderer.shadowMapEnabled = true;var cubeGeometry = new THREE.BoxGeometry(20, 20, 20);var meshMaterial1 = createMaterial("vertex-shader", "fragment-shader-1");var meshMaterial2 = createMaterial("vertex-shader", "fragment-shader-2");var meshMaterial3 = createMaterial("vertex-shader", "fragment-shader-3");var meshMaterial4 = createMaterial("vertex-shader", "fragment-shader-4");var meshMaterial5 = createMaterial("vertex-shader", "fragment-shader-5");var meshMaterial6 = createMaterial("vertex-shader", "fragment-shader-6");var material = new THREE.MeshFaceMaterial([meshMaterial1,meshMaterial2,meshMaterial3,meshMaterial4,meshMaterial5,meshMaterial6]);
// var material = new THREE.MeshFaceMaterial([meshMaterial2, meshMaterial2, meshMaterial1, meshMaterial1, meshMaterial1, meshMaterial1]);var cube = new THREE.Mesh(cubeGeometry, material);// 场景中添加立方体网格对象scene.add(cube);// 设置相机位置和方向camera.position.x = 30;camera.position.y = 30;camera.position.z = 30;camera.lookAt(new THREE.Vector3(0, 0, 0));// 添加环境光源var ambientLight = new THREE.AmbientLight(0x0c0c0c);scene.add(ambientLight);// 添加聚光灯光源,设置位置和投影var spotLight = new THREE.SpotLight(0xffffff);spotLight.position.set(-40, 60, -10);spotLight.castShadow = true;scene.add(spotLight);document.getElementById("WebGL-output").appendChild(renderer.domElement);var step = 0;render();function render() {stats.update();// 立方体旋转动画cube.rotation.y = step += 0.01;cube.rotation.x = step;cube.rotation.z = step;// 着色器更新cube.material.materials.forEach(function (e) {e.uniforms.time.value += 0.01;});requestAnimationFrame(render);renderer.render(scene, camera);}function initStats() {var stats = new Stats();stats.setMode(0);stats.domElement.style.position = 'absolute';stats.domElement.style.left = '0px';stats.domElement.style.top = '0px';document.getElementById("Stats-output").appendChild(stats.domElement);return stats;}function createMaterial(vertexShader, fragmentShader) {var vertShader = document.getElementById(vertexShader).innerHTML;var fragShader = document.getElementById(fragmentShader).innerHTML;var attributes = {};var uniforms = {time: {type: 'f', value: 0.2},scale: {type: 'f', value: 0.2},alpha: {type: 'f', value: 0.6},resolution: {type: "v2", value: new THREE.Vector2()}};uniforms.resolution.value.x = window.innerWidth;uniforms.resolution.value.y = window.innerHeight;var meshMaterial = new THREE.ShaderMaterial({uniforms: uniforms,attributes: attributes,vertexShader: vertShader,fragmentShader: fragShader,transparent: true});return meshMaterial;}}window.onload = init;
</script>
</body>
</html>
效果如下: