👨??? 主頁: 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>
效果如下: