THREE.ShaderMaterial 添加纹理 sampler2D texture
研究一下ShaderMaterial,主要包含以下几个功能。
- 给plane添加纹理
- 用灰度图生成地形
- 用简单的函数生成水波
- 让水波流动起来
- 设置多张纹理
- 动态改变纹理的颜色
- 用法线改变物体的颜色
效果如下,场景中主要又三个物体,plane1,plane2,Sphere。
plane1: 演示给plane添加纹理和生成高度。
plane2:演示水波,高度、多张纹理,水面流动。
Sphere:法向量。
plane1
生成一个平面,采用一张纹理贴图,顺便读取贴图的rgb值,并把值赋值到高度上,给plane添加高度,这个方法可以采用灰度图,生成地形。
<script type="x-shader/x-vertex" id="vertexshaderA">
varying vec2 vUv;
uniform sampler2D texture;
void main() {
vUv = uv;
vec4 t = texture2D( texture, vUv );
gl_Position = projectionMatrix * modelViewMatrix * vec4( position.x,position.y,position.z+t.g, 1.0 );
}
</script>
<script type="x-shader/x-fragment" id="fragmentshaderA">
varying vec2 vUv;
uniform sampler2D texture;
void main() {
gl_FragColor = texture2D( texture, vUv );
}
</script>
uniformsA = {
amplitude: {value: 1.0},
color: {value: new THREE.Color(0xff2200)},
texture: {value: new THREE.TextureLoader().load("../img/bg1024.jpg")}
};
uniformsA.texture.value.wrapS = uniformsA.texture.value.wrapT = THREE.RepeatWrapping;
var shaderMaterial = new THREE.ShaderMaterial({
uniforms: uniformsA,
vertexShader: document.getElementById('vertexshaderA').textContent,
fragmentShader: document.getElementById('fragmentshaderA').textContent,
side: THREE.DoubleSide
});
var planeGeoA = new THREE.PlaneGeometry(40, 40, 80, 80);
var planeA = new THREE.Mesh(planeGeoA, shaderMaterial);
planeA.position.y = -5;
planeA.rotation.x = -0.5 * Math.PI;
scene.add(planeA);
代码中关键信息:
uv: 纹理坐标,内置变量
vec4 t = texture2D( texture, vUv ) ,返回纹理坐标对应的纹理点颜色值,vec4(r,g,b,w)
projectionMatrix * modelViewMatrix * vec4( position, 1.0 ) 投影矩阵x模型视图矩阵=总变换矩阵。position内置变量。
plane2
打开plane2的网格显示,可以更加清晰的看出网格的变化。中间的花边形状的凸起是用的一张花瓣的灰度图生成的。整体的起伏是用三角函数生成的。在three.js中Water.js中水的实现方式也是类似,只是噪波函数更加复杂,还有加上了法向量,来反射光线,显得更加真实。凸起的部分使用了另外一张纹理图,按照高度来区分的。
THREE.Shadertest = {
uniforms: {
uStartAngle: {value: 0.0},
textureSampler: {value: new THREE.TextureLoader().load("../img/water.jpg")},
texturebg12: {value: new THREE.TextureLoader().load("../img/bg11.jpg")},
texturea: {value: new THREE.TextureLoader().load("../img/waternormals.jpg")},
},
vShaderPlane: [
// 'uniform mat4 mvp_matrix;', // 总变换矩阵 用这个 projectionMatrix * modelViewMatrix
'uniform float uStartAngle;', // 起始角度
'varying vec2 vTextureCoord;',
'varying vec2 vUv;', //传递给片元的纹理坐标
'varying vec4 q_Position;',
'uniform sampler2D texturebg12;', // 纹理
'void main(){',
' vUv = uv;',
' vec4 t = texture2D( texturebg12, vUv);',
' float uWidthSpan = 40.0;',
' float angleSpanh = 20.0*3.14159265;',
' float startX = -uWidthSpan/2.0;',
' float currAngleZ = uStartAngle*1.0 +((position.x-startX)/uWidthSpan)*angleSpanh;',
' float currAngleY = uStartAngle*1.0+2.0 +((position.y-startX)/uWidthSpan)*angleSpanh;',
' float tz = sin(currAngleZ)*0.2;',
' float ty = sin(currAngleY)*0.1;',
' gl_Position = projectionMatrix * modelViewMatrix * vec4(position.x,position.y, tz+ty+t.r/0.8, 1.0);',
' q_Position = modelMatrix * vec4(position.x,position.y, tz+ty+t.r/0.4, 1.0);',
'}'
].join('\n'),
fShaderPlane: [
'uniform float uStartAngle;', // 起始角度
'uniform sampler2D textureSampler;', // 纹理
'uniform sampler2D texturea;', // 纹理
'varying vec2 vUv;', //传递给片元的纹理坐标
'varying vec4 q_Position;',
'void main(){',
'vec4 tc = texture2D( textureSampler, vUv) ;',
'if(q_Position.y >= 0.8){gl_FragColor = vec4(tc.r*sin(uStartAngle),tc.g,tc.b, 1.0);}',
'if(q_Position.y < 0.8) {gl_FragColor = texture2D( texturea, vUv);}',
'}'
].join('\n')
};
var planeGeo = new THREE.PlaneGeometry(40, 40, 120, 120);
var planeMat = new THREE.ShaderMaterial({
uniforms: THREE.Shadertest.uniforms,
vertexShader: THREE.Shadertest.vShaderPlane,
fragmentShader: THREE.Shadertest.fShaderPlane,
side: THREE.DoubleSide,
});
plane = new THREE.Mesh(planeGeo, planeMat);
plane.rotation.x = -0.5 * Math.PI;
scene.add(plane);
plane.material.uniforms.uStartAngle.value += 0.05;
球体Sphere
THREE.Shadertest = {
uniforms: {
coeficient: {value: 1.0},
power: {value: 1.0},
},
vertexShader: [
'varying vec3 vVertexWorldPosition;',
'varying vec3 vVertexNormal;',
'varying vec4 vFragColor;',
'uniform vec3 ec_light_dir;',
'varying float v_diffuse; ',
'void main(){',
' vVertexNormal = normalize(normalMatrix * normal);',
' vVertexWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;',
' v_diffuse = max(dot(ec_light_dir,vVertexNormal),0.0);',
' gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);',
'}'
].join('\n'),
fragmentShader: [
'uniform float coeficient;',
'uniform float power;',
'varying vec3 vVertexNormal;',
'varying vec3 vVertexWorldPosition;',
'varying vec4 vFragColor;',
'varying float v_diffuse; ',
'void main(){',
' vec3 worldCameraToVertex= vVertexWorldPosition - cameraPosition;',
' vec3 viewCameraToVertex = (viewMatrix * vec4(worldCameraToVertex, 0.0)).xyz;',
' viewCameraToVertex = normalize(viewCameraToVertex);',
' float intensity = pow(coeficient + dot(vVertexNormal, viewCameraToVertex), power);',
' gl_FragColor = vec4(vVertexNormal, 1.0);',
'}'
].join('\n'),
};
var geometry = new THREE.SphereGeometry(r, 30, 30);
var material = new THREE.ShaderMaterial({
uniforms: THREE.Shadertest.uniforms,
vertexShader: THREE.Shadertest.vertexShader,
fragmentShader: THREE.Shadertest.fragmentShader,
});
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);