THREE.ShaderMaterial 添加纹理sampler2D texture

时间:2024-04-07 12:34:29

THREE.ShaderMaterial 添加纹理 sampler2D texture

研究一下ShaderMaterial,主要包含以下几个功能。

  1. 给plane添加纹理
  2. 用灰度图生成地形
  3. 用简单的函数生成水波
  4. 让水波流动起来
  5. 设置多张纹理
  6. 动态改变纹理的颜色
  7. 用法线改变物体的颜色

效果如下,场景中主要又三个物体,plane1,plane2,Sphere。
plane1: 演示给plane添加纹理和生成高度。
plane2:演示水波,高度、多张纹理,水面流动。
Sphere:法向量。
THREE.ShaderMaterial 添加纹理sampler2D texture

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.ShaderMaterial 添加纹理sampler2D texture

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);