webGL: 曲师师AR项目总结

1,794 阅读3分钟

介绍

项目链接(需要网易新闻客户端最新版)

AR录屏

前几周做了一个AR小项目,主要功能就是展示模型动画,想做一个分享,但是AR是基于APP的,不太好分享~。。脑壳疼~。。好在AR其实本质就是3d,就想着用webGL讲讲里面的一些东西。

项目逻辑主要是 搜索平面-> 创建模型-> 模型操作(矩阵变换以及动画播放)

其中,创建模型和模型的缩放、位移、移动以及播放相应模型动画这些不做解释,完全可以用threejs、babylonjs去找对应API,这篇文章主要是讲搜索平面所用到的材质是怎么做的。

平面提示材质

这里的逻辑是捕捉到一个有效平面后出现一个具有延申效果的平面提示材质。

  • 平面捕捉使用的是AR SDK提供的api。

平面捕捉

材质制作思路:

  • 创建地面Mesh -> 材质添加遮罩层处理 -> 材质添加白点素材,并做repeat处理 -> 传入mesh坐标用作白点素材的偏移量

  1. 创建地面mesh
    //babylon.js
    this.tracking = BABYLON.Mesh.CreateGround('Ground', 1, 1, 1, this.scene);
  1. 自定义材质

    由于开发文档没有需要的材质所以我选择自定义材质。

    //shaderCode
    BABYLON.Effect.ShadersStore["trackingVertexShader"] = require('./tracking.vert');
    BABYLON.Effect.ShadersStore["trackingFragmentShader"] = require('./tracking.frag');
    this.tracking.material = new BABYLON.ShaderMaterial("trackingShader", this.scene, {
        vertex: "tracking",
        fragment: "tracking",
    },
    { 
        attributes: ["position", "normal", "uv"],
        uniforms: ["world", "worldView", "worldViewProjection", "view", "projection","uPosition","uTexture","uTime"]
        //uniform 我只用到了 uPosition,uTexture,uTime 分别是mesh的位置,白点素材和时间
    });
    //顶点着色器
    precision highp float;
    attribute vec3 position;
    attribute vec2 uv;
    uniform mat4 worldViewProjection;
    varying vec2 vUv;
    void main(void) {
        vec3 _position = position*vec3(1.0,0.0,1.0);
        gl_Position = worldViewProjection * vec4(_position, 1.0);
        vUv = uv;
    }

其中顶点着色器没啥说的,传入的uniform都是传给片元着色器的。

    //片元着色器
    precision highp float;
    varying vec2 vUv;
    uniform sampler2D uTexture; //白点素材
    uniform vec3 uPosition;     //当前mesh的位置
    uniform float uTime;        //时间
    void main(void) {
        float _distance = distance(vec2(0.5),vUv);
        //mask 第一步:半径超过0.5的颜色丢弃;
        if(_distance<.5){
                //白点:基于mesh位置做偏移,可以实现移动mesh时 白点相对于世界的位置不变。
                vec2 uv = vUv*5.+uPosition.xz*5.0/5.0;
                //白点:做repeat处理
                vec2 uv = mod(abs(uv),1.0);
                //这一段判断不用在意 我这个图片不知道为啥边缘会有一条线。。
                if(uv.x>0.05&&uv.x<0.95&&uv.y>0.05&&uv.y<0.95){
                    vec4 color = texture2D(uTexture,uv);
                    //mask 第二步:做透明度渐变mesh;
                    gl_FragColor = color*vec4(vec3(1),1.0-pow(_distance*2.,2.));
                    if(gl_FragColor.a==0.0) discard;
                }else{
                    discard;
                }
        }else{
            discard;
        }
    }

剩下的就是传入uniform数据就可以了。

    //uTexture
    const texture = new BABYLON.Texture(require('../assets/Artboard.png'), this.scene);
    texture.wrapU = BABYLON.Texture.MIRROR_ADDRESSMODE;
    texture.wrapV = BABYLON.Texture.MIRROR_ADDRESSMODE;
    (<BABYLON.ShaderMaterial>this.tracking.material).setTexture("uTexture", texture);
    //uPosition 
    //this.tracking.position -> BABYLON.Vector3
    (<BABYLON.ShaderMaterial>this.tracking.material).setVector3('uPosition',this.tracking.position);

效果如下(白点有延申效果且每个白点相对于世界静止的):

附加项

感觉放在webGL不是很好看,哈哈哈,随便改了改片元着色器,根据时间做了一个涟漪的效果。

    //片元着色器
    precision highp float;
    varying vec2 vUv;
    uniform sampler2D uTexture;
    uniform vec3 uPosition;
    uniform float uTime;
    void main(void) {
        float _distance = distance(vec2(0.5),vUv);
        if(_distance<.5){
                //涟漪start
                vec2 offset = vec2(0);
                float ripple = mod(uTime/1000.,1.5)-_distance;
                if(abs(ripple)<.1){
                    offset=normalize(vUv-vec2(0.5))*-1.*(cos(ripple*31.42)+1.)/8.;
                }
                vec2 uv = mod(abs(vUv*5.+offset+uPosition.xz*5.0/5.0),1.0);
                //涟漪end
                if(uv.x>0.05&&uv.x<0.95&&uv.y>0.05&&uv.y<0.95){
                    vec4 color = texture2D(uTexture,uv);
                    gl_FragColor = color*vec4(vec3(1),1.0-pow(_distance*2.,2.));
                    if(gl_FragColor.a==0.0) discard;
                }else{
                    discard;
                }
        }else{
            discard;
        }
    }

涟漪效果如下:

demo link

ps:场景是自己实现的,觉得windows 10 3d查看器的场景挺好看的就仿照做了一个。哈哈。