注意

看这一篇文章最好有webgl基础的同学看,如果没有webgl原生基础,你会看得很懵逼。

简介

THREE.ShaderMaterial是Three.js库中最通用、最复杂的材质之一。通过它可以使用自己定制的着色器。直接在webgl环境中运行。着色器可以将Three.js中的JavaScript网格转换为屏幕上的像素。通过这些自定义的着色器,可以明确地指定对象如何渲染,以及如何覆盖或修改Three.js库中的默认值。

特别属性

THREE.ShaderMaterial有一些前面见过的并且正常的属性如wireframe、linewidth、shading、vertexColors、fog以外,还有一些特别的属性,可以用来定制自己的着色器:

名称描述
fragmentShader这个属性是用来定义自己的片元着色器的
vertexShader这个属性是定义自己的顶点着色器的
uniforms通过这个属性可以给着色器传入uniform变量的值
defaultAttributeValues(原来为:attributes)该属性用于传递attribute变量的的值
defines转换成#define代码片段。这些片段可以用来设置着色器程序里的一些额外的全局变量
lights该属性定义光照数据是否传递给着色器,默认值为false

一个实例化案例

var material = new THREE.ShaderMaterial( {

    uniforms: {
        time: { value: 1.0 },
        resolution: { value: new THREE.Vector2() }
    },
    attributes: {
        vertexOpacity: { value: [] }
    },
    vertexShader: document.getElementById( 'vertexShader' ).textContent,
    fragmentShader: document.getElementById( 'fragmentShader' ).textContent

} );

一个可运行的代码案例

案例查看地址:http://www.wjceo.com/blog/threejs/2018-02-12/40.html

<!doctype html>
<html lang="cn">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>ShaderMaterial案例</title>
    <script src="https://johnson2heng.github.io/three.js-demo/lib/three.js"></script>
    <script src="https://johnson2heng.github.io/three.js-demo/lib/js/libs/stats.min.js"></script>
    <script src="https://johnson2heng.github.io/three.js-demo/lib/js/libs/dat.gui.min.js"></script>
    <style type="text/css">
        html, body {
            margin: 0;
            height: 100%;
        }

        canvas {
            display: block;
        }

    </style>
</head>
<body onload="draw()">
<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.5

    void 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">
    // from http://glsl.heroku.com/e#7906.0


    uniform float time;
    uniform vec2 resolution;

    // 2013-03-30 by @hintz

    #define CGFloat float
    #define M_PI 3.14159265359

    vec3 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)
    {
    return
    fract(
    (
    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 center

    uPos.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>
</body>
<script>
    var renderer;
    function initRender() {
        renderer = new THREE.WebGLRenderer({antialias: true});
        renderer.setSize(window.innerWidth, window.innerHeight);
        //告诉渲染器需要阴影效果
        renderer.shadowMap.enabled = true;
        renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 默认的是,没有设置的这个清晰 THREE.PCFShadowMap
        document.body.appendChild(renderer.domElement);
    }

    var camera;
    function initCamera() {
        camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
        camera.position.set(30,30,30);
        camera.lookAt(new THREE.Vector3(0, 0, 0));
    }

    var scene;
    function initScene() {
        scene = new THREE.Scene();
    }

    //初始化dat.GUI简化试验流程
    var gui;
    function initGui() {
        //声明一个保存需求修改的相关数据的对象
        gui = {
        };
        var datGui = new dat.GUI();
        //将设置属性添加到gui当中,gui.add(对象,属性,最小值,最大值)
    }

    var ambientLight,spotLight;
    function initLight() {
        ambientLight = new THREE.AmbientLight(0x0c0c0c);
        scene.add(ambientLight);

        spotLight = new THREE.SpotLight(0xffffff);
        spotLight.position.set(-40, 60, -10);
        spotLight.castShadow = true;
        scene.add(spotLight);
    }

    var cube;
    function initModel() {
        //声明一个立方体几何图形
        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 = [meshMaterial6, meshMaterial5, meshMaterial4, meshMaterial3, meshMaterial2, meshMaterial1];

        //实例化一个网格
        cube = new THREE.Mesh(cubeGeometry, material);

        //将网格添加到场景当中
        scene.add(cube);
    }

    //初始化性能插件
    var stats;
    function initStats() {
        stats = new Stats();
        document.body.appendChild(stats.dom);
    }

    var step = 0;
    function render() {
        cube.rotation.y = step += 0.01;
        cube.rotation.x = step;
        cube.rotation.z = step;


        cube.material.forEach(function (e) {
            e.uniforms.time.value += 0.01;
        });

        renderer.render(scene, camera);
    }

    //窗口变动触发的函数
    function onWindowResize() {

        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        render();
        renderer.setSize(window.innerWidth, window.innerHeight);

    }

    function animate() {
        //更新控制器
        render();

        //更新性能插件
        stats.update();

        requestAnimationFrame(animate);
    }

    function draw() {
        initGui();
        initRender();
        initScene();
        initCamera();
        initLight();
        initModel();
        initStats();

        animate();
        window.onresize = onWindowResize;
    }

    //创建ShaderMaterial纹理的函数
    function createMaterial(vertexShader, fragmentShader) {
        var vertShader = document.getElementById(vertexShader).innerHTML; //获取顶点着色器的代码
        var fragShader = document.getElementById(fragmentShader).innerHTML; //获取片元着色器的代码

        //配置着色器里面的attribute变量的值
        var attributes = {};
        //配置着色器里面的uniform变量的值
        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(window.innerWidth, window.innerHeight)}
        };

        var meshMaterial = new THREE.ShaderMaterial({
            uniforms: uniforms,
            defaultAttributeValues : attributes,
            vertexShader: vertShader,
            fragmentShader: fragShader,
            transparent: true

        });


        return meshMaterial;
    }
    </script>
</html>

着色器代码是偷的《three.js开发指南》里面的,自己整理了一个代码,想学习这个的小朋友可以看一下。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐