评论

小程序webgl shader动效演示

小程序webgl shader动效演示



前言: 如果大家对webgl图形学感兴趣请留言回复,多的话我可能会写一些系列教程文章。


微信2.7版本新增webgl支持,开发版工具nightly已可调试

试用了一下  暂时发现几个问题:


1.移动端应避免大尺寸纹理  我的机器似乎2048以上就不支持, 没具体测试。

2.不支持 OES_standard_derivatives 拓展,这个拓展可以在webgl1下shader中使用以下函数

genType dFdx(genType)  genType dFdy(genType)   genType fwidth(genType)

,所以我们不去加载该拓展就好了 shader中也不要使用这些函数(这个问题在小游戏中也存在),

提到webgl,很多伙伴就想到Three.js,

实际上还有很多 像Babylon.js  playcanvas scene.js等等 优秀的框架

整体使用下来,本人推荐playcanvas。

playcanvas整个框架逻辑完全照搬unity,有强大的网页编辑工具。比起Three.js有这更好的游戏功能支持,以及更优秀的资源脚本系统

有丰富的拓展插件功能。


回到主题,小程序中使用webgl需要一个canvas


<canvas type="webgl" id="application" style="width:100%;height:{{boxHeight}}px;" wx:if="{{canvasready}}"></canvas>


const query = wx.createSelectorQuery()

query.select('#application').node().exec((res) => {

   const canvas = res[0].node ;

})


通过createSelectorQuery拿到canvas实例,我们很多的方法都是 挂载在canvas上的,

像创建Image实例  使用canvas.createImage

像raf   使用 canvas.requestAnimationFrame


所以我们在改造引擎使得能够在微信小程序中工作,这些地方都要做相应的改动,我们改造的时候依然暴露出全局对象pc;

首先给canvas补上类dom元素的一些方法属性

canvas加上getBoundingClientRect方法,以及宽高


canvas.devicePixelRatio = systemInfo.pixelRatio;

canvas.clientWidth = systemInfo.windowWidth;

canvas.clientHeight = systemInfo.windowHeight;

canvas.width = canvas.clientWidth;

canvas.height = canvas.clientHeight;

_defineProperty(canvas, 'getBoundingClientRect', {

value: function () {

     return {

top: 0,

left: 0,

width: canvas.width,

height: canvas.height

}

},

writable: false,

configurable: true

})


pc对象上添加创建图片对象的方法,我这里加上instanceof用来判断资源类型(有时候需要区分canvas还是image)

pc.createImage = function () {

   var image = canvas.createImage();

image.instanceof = 'HTMLImageElement';

   return image;

}

app.canvas.requestAnimationFrame(app.tick);



var application = new pc.Application(canvas, { assetPrefix: 'anonymous'});

application.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);

application.setCanvasResolution(pc.RESOLUTION_AUTO);


//在web中  Image配置crossOrigin 在小程序中似乎没有必要了  ,但是不影响我们依然加上  assetPrefix



创建一个相机,这个示例主要演示shader,不是3d模型,这里用正交投影相机

var camera = new pc.Entity();
camera.addComponent('camera', {
nearClip: 0,
farClip: 2,
projection: pc.PROJECTION_ORTHOGRAPHIC,
clearColor: new pc.Color(0, 0, 0)
});
camera.setPosition(0, 0, 1);
var CameraAspect = pc.createScript('cameraAspect');
CameraAspect.prototype.initialize = function () {
this.currentOrthoHeight = this.entity.camera.orthoHeight;
};
CameraAspect.prototype.update = function (t) {
var canvas = this.app.graphicsDevice.canvas;
var aspectRatio = canvas.width / canvas.height;
var orthoHeight = pc.math.clamp(0.72 / aspectRatio, 1, 1.28);
if (orthoHeight !== this.currentOrthoHeight) {
this.entity.camera.orthoHeight = orthoHeight;
this.currentOrthoHeight = orthoHeight;
}
};
camera.addComponent('script');
camera.script.create('cameraAspect');
application.root.addChild(camera);



//创建一个平面


var plane = new pc.Entity();

plane.addComponent('model', {type: "plane"});

//TRS

plane.setLocalPosition(0, -0.006,0);

plane.setEulerAngles(90, 0, 0);

plane.setLocalScale(1.54, 1.05, 2.61);

//给平面引入脚本系统

plane.addComponent('script');



//shader脚本 cardManShader 如果在web端我们其实可以当成资源引入,可惜小程序中不支持动态脚本  


var CardManShader = pc.createScript('cardManShader');
CardManShader.attributes.add('vs', {
type: 'asset',
assetType: 'shader',
title: 'Vertex Shader'
});

CardManShader.attributes.add('fs', {
type: 'asset',
assetType: 'shader',
title: 'Fragment Shader'
});

CardManShader.attributes.add('uTexture', {
type: 'asset',
assetType: 'texture',
title: '火焰纹理'
});

CardManShader.attributes.add('uNormalTexture', {
type: 'asset',
assetType: 'texture',
title: '宝剑的remap 纹理'
});

CardManShader.attributes.add('uFlowTexture', {
type: 'asset',
assetType: 'texture',
title: 'wave纹理'
});

CardManShader.attributes.add('uCardTexture', {
type: 'asset',
assetType: 'texture',
title: '卡片纹理'
});

CardManShader.attributes.add('uMaskTexture', {
type: 'asset',
assetType: 'texture',
title: 'man遮罩纹理'
});

CardManShader.attributes.add('uMainTexture', {
type: 'asset',
assetType: 'texture',
title: 'man纹理'
});

CardManShader.prototype.initialize = function () {

this.time = 0;
this.material = new pc.Material();


this.uTextureMap = this.uTexture.resource;
this.uNormalTextureMap = this.uNormalTexture.resource;
this.uFlowTextureMap = this.uFlowTexture.resource;
this.uCardTextureMap = this.uCardTexture.resource;


this.uMaskTextureMap = this.uMaskTexture.resource;
this.uMainTextureMap = this.uMainTexture.resource;



this.entity.model.model.meshInstances[0].material = this.material;
this.createShader();

};

CardManShader.prototype.update = function (dt) {
this.time += dt * 2;
this.material.setParameter('uTime', this.time);
};

CardManShader.prototype.createShader = function () {
var app = this.app;
var model = this.entity.model.model;
var vertexShader = this.vs.resource;
var fragmentShader = this.fs.resource;
var shaderDefinition = {
attributes: {
aPosition: pc.SEMANTIC_POSITION,
aNormal: pc.SEMANTIC_NORMAL,
aUv: pc.SEMANTIC_TEXCOORD0
       },
vshader: vertexShader,
fshader: fragmentShader
   };
var gd = app.graphicsDevice;
this.shader = new pc.Shader(gd, shaderDefinition);

this.material.shader = this.shader;
this.material.setParameter('uTime', 0);

this.material.setParameter('uTexture', this.uTextureMap);
this.material.setParameter('uNormalTexture', this.uNormalTextureMap);
this.material.setParameter('uFlowTexture', this.uFlowTextureMap);
this.material.setParameter('uCardTexture', this.uCardTextureMap);


this.material.setParameter('uMaskTexture', this.uMaskTextureMap);

this.material.setParameter('uMainTexture', this.uMainTextureMap);
};

CardManShader.prototype.updateShader = function (t1, t2, t3, t4, t5, t6) {

if (t1) this.uTexture = t1;
this.uTextureMap = this.uTexture.resource;
if(t2) this.uNormalTexture = t2;
this.uNormalTextureMap = this.uNormalTexture.resource;
if (t3) this.uFlowTexture = t3;
this.uFlowTextureMap = this.uFlowTexture.resource;
if(t4) this.uCardTexture = t4;
this.uCardTextureMap = this.uCardTexture.resource;
if(t5) this.uMaskTexture = t5;
this.uMaskTextureMap = this.uMaskTexture.resource;
if (t6) this.uMainTexture = t6;
this.uMainTextureMap = this.uMainTexture.resource;


var material = this.entity.model.model.meshInstances[0].material;

material.setParameter('uTexture', this.uTextureMap);
material.setParameter('uNormalTexture', this.uNormalTextureMap);
material.setParameter('uFlowTexture', this.uFlowTextureMap);
material.setParameter('uCardTexture', this.uCardTextureMap);
material.setParameter('uMaskTexture', this.uMaskTextureMap);
material.setParameter('uMainTexture', this.uMainTextureMap);
};






//给shader脚本 加上着色器 以及贴图资源

var vsglsl = new pc.Asset("vs", "shader");


vsglsl.resource = `precision ${application.graphicsDevice.precision} float;

attribute vec4 aPosition;

attribute vec3 aNormal;

attribute vec2 aUv;

uniform mat4   matrix_viewProjection;

uniform mat4   matrix_model;

uniform mat4   matrix_view;

uniform mat3   matrix_normal;

uniform vec3   uLightPos;

     

       // Color to fragment program

varying float vertOutTexCoord;

varying vec2 texCoord;

void main(void)

{

           mat4 modelView = matrix_view * matrix_model;

           mat4 modelViewProj = matrix_viewProjection * matrix_model;

           vec3 eyeNormal = normalize(matrix_normal * aNormal);

           vec4 vertexPos = modelView * aPosition;

           vec3 vertexEyePos = vertexPos.xyz / vertexPos.w;

           vec3 lightDir = normalize(uLightPos - vertexEyePos);

           vertOutTexCoord = max(0.0, dot(eyeNormal, lightDir));

           texCoord = aUv;

           gl_Position = modelViewProj * aPosition;

}`;



var fsglsl = new pc.Asset("fs", "shader");

fsglsl.resource = `precision ${application.graphicsDevice.precision} float;

uniform sampler2D uTexture;

uniform sampler2D uNormalTexture;

uniform sampler2D uFlowTexture;

uniform sampler2D uCardTexture;

       

uniform sampler2D uMaskTexture;

uniform sampler2D uMainTexture;

uniform float  uDistortPower;

uniform float  uTime;

varying float vertOutTexCoord;

varying vec2 texCoord;


vec3 unpackNormal(sampler2D nmap, vec2 uv) {

   return texture2D(nmap,uv).xyz*2.0-vec3(1.0);

}



vec3 unpackNormal(vec4 color) {

   return color.xyz*2.0-vec3(1.0);

}



vec3 packNormal(vec3 n) {

   return (n+vec3(1.0))*0.5;

}


vec2 distort(float mask,sampler2D distTex, vec2 distUV, float distPower) {

    vec4 distColor =  texture2D(distTex, distUV +  vec2(uTime) * 0.5);

    vec2 offsetUV = (mask * unpackNormal(distColor) * distPower).st;

    return offsetUV;

}



vec2 rotate(vec2 uv, vec4 param,float time) {

    float cosx = cos(param.z + param.w * time);

    float sinx = sin(param.z + param.w * time);

    mat2 m2 = mat2(cosx, sinx, -sinx, cosx);

    return m2 * (uv.xy - param.xy) + param.xy;

}


 vec2 UV_RotateAround(vec2 center,vec2 uv,float rad){

      vec2 fuv = uv - center;

      mat2 ma = mat2(cos(rad),sin(rad),-sin(rad),cos(rad));

      fuv = ma * fuv + center;

      return fuv;

}

       


 vec2 UV_STD2Rect(vec2 uv,vec4 rect){

      uv.x = mix(rect.x,rect.x+rect.z, uv.x);

      uv.y = mix(rect.y,rect.y+rect.w, uv.y);

      return uv;

    }



       vec4  remap(sampler2D remapTex,vec2 uv){

           vec2  uv_remap2 = uv;

            vec4  color_remap2 = texture2D(remapTex,uv_remap2);    

            if(color_remap2.b >= 0.5) {

               color_remap2 = vec4(0,color_remap2.gba);

           }  

           return color_remap2;

       }


       const vec4  _Color_image1 = vec4(1.,1.,1.,1.);

              

       vec2 imagenodeBase(vec2 uv_image,vec4 uv_tint,vec3 uv_move,float uv_rotate){


           vec2 tilling = uv_tint.xy;

           vec2 offset = uv_tint.zw;


           vec2 move = uv_move.xy;

           float moveFactor = uv_move.z;

           float rotaterad = uv_rotate;



          vec2 center_image = vec2(0.5,0.5);

          uv_image = uv_image-center_image;

          uv_image = uv_image+offset;


       //trs

          uv_image = uv_image+move*moveFactor;

          uv_image = UV_RotateAround(vec2(0.,0.),uv_image,rotaterad);

          uv_image = uv_image/tilling;


       // vec2 dir_image = uv_image/length(uv_image);

       // uv_image = uv_image-dir_image*vec2(0.,0.)*(_Time.y); //spin


       // uv_image = UV_RotateAround(vec2(0.,0.),uv_image,0.*(_Time.y)); //radial



          uv_image = uv_image + center_image;


          return uv_image;

       }




       vec2  uvnode(sampler2D _waveTex,vec2 uv){

           vec2 _Time = vec2(uTime,uTime);

           vec2 tilling = vec2(0.60546,0.60913);

           vec2 offset = vec2(0.,4.17232);


           vec2 move = vec2(0.07617,0.);

           float moveFactor = _Time.y;

           float rotaterad = 0.;


           vec2 ruv = vec2(0.,0.1503901);

           vec2 guv = vec2(0.24501,0.00114);

           vec2 buv = vec2(0.,0.);

           vec2 auv = vec2(0.,0.);


           vec2  uv_uv = uv;


           uv_uv = imagenodeBase(uv_uv, vec4(tilling,offset),vec3(move,moveFactor),rotaterad);


            vec4 rect_uv =  vec4(1,1,1,1);

            vec4 color_uv = texture2D(_waveTex,uv_uv);    

           uv_uv = -(color_uv.r*ruv + color_uv.g*guv + color_uv.b*buv +  color_uv.a*auv);  

           

           return uv_uv;

       }



       vec2  uvnodehair(sampler2D _waveTex,vec2 uv){

           vec2 _Time = vec2(uTime,uTime);

           vec2 tilling = vec2(0.34375,0.3339844);

           vec2 offset = vec2(0.2,0.);


           vec2 move = vec2(0.,-0.1328125);

           float moveFactor = _Time.y;

           float rotaterad = 0.;


           vec2 ruv = vec2(-0.02734375,0.02539063);

           vec2 guv = vec2(0.02148438,0.02539063);

           vec2 buv = vec2(0.,0.);

           vec2 auv = vec2(0.,0.);


           vec2  uv_uv = uv;


           uv_uv = imagenodeBase(uv_uv, vec4(tilling,offset),vec3(move,moveFactor),rotaterad );


            vec4 rect_uv =  vec4(1,1,1,1);

            vec4 color_uv = texture2D(_waveTex,uv_uv);    

           uv_uv = -(color_uv.r*ruv + color_uv.g*guv + color_uv.b*buv +  color_uv.a*auv);  

           

           return uv_uv;

       }



     

       vec4 imagenode(sampler2D _flameTex, vec4 color_remap,vec2 newuv,vec2 olduv){

           vec2 _Time = vec2(uTime,uTime);


           vec2 tilling = vec2(0.5,1.);

           vec2 offset = vec2(0.,0.);

           vec2 move = vec2(-0.06640625,0);

           float moveFactor = _Time.y;

           float rotaterad = 0.;


           vec2 uv_image = color_remap.rg;

                

           uv_image = imagenodeBase(uv_image,vec4(tilling,offset),vec3(move,moveFactor),rotaterad);

            uv_image = uv_image + newuv*1.*((1.));

            vec4 rect_image =  vec4(1.,1.,1.,1.);

            vec4 color_image = texture2D(_flameTex,uv_image);    

           //color_image = color_image * _Color_image3;

           

           //blendadd

           color_image = vec4(color_image.rgb,color_image.a*color_remap.a);

           

           return color_image;

       }


       void main(void)

       {


           vec2 _Time = vec2(uTime,uTime);

           // float v = vertOutTexCoord;

           // v = float(int(v * 6.0)) / 6.0;


           vec4 color_xman_mask0 = texture2D(uMaskTexture,texCoord);

           vec2 uv = texCoord;

           vec4 color = texture2D (uTexture, uv);



           vec2 color_ROOTUV = imagenodeBase(texCoord, vec4(0.9550781,0.5,0.02246094,-0.2177734),

                  vec3(0.,0.,uTime),0.);

         

           vec4 remapuv = remap(uNormalTexture,color_ROOTUV);

           vec2 newuv =  uvnode(uFlowTexture,color_ROOTUV);

           vec4 flowcolor =  imagenode(uTexture,remapuv,newuv,color_ROOTUV);


           vec2 newuvhair =  uvnodehair(uFlowTexture,color_ROOTUV);


           vec4 offsetx_color_xman_mask0 = texture2D(uMaskTexture,texCoord + vec2(-0.1,-0.02));



           vec2 color_ROOT2 = color_ROOTUV + newuvhair *1.*((1.)) * offsetx_color_xman_mask0.b;


           vec4 color_ROOT = texture2D(uMainTexture,color_ROOT2);


         

           color = mix(color_ROOT,vec4(flowcolor.rgb,1.),clamp(flowcolor.a*1.*((1.)),0.,1.));


            vec4 cardcolor = texture2D(uCardTexture,texCoord);

            color = mix(cardcolor,vec4(color.rgb,1.),clamp(color.a*1.*((1.))  * color_xman_mask0.r,0.,1.));

           //gl_FragColor = color_ROOT;

            gl_FragColor = color;

       }`



var uTextureAsset = new pc.Asset("uTexture", "texture", {

url: "http://p1.qhimgs3.com/t01cd1ec98a32680eba.png"

});

var uNormalTextureAsset = new pc.Asset("uNormalTexture", "texture", {

url: "http://p1.qhimgs3.com/t01396cc020f83752f8.png"

});

var uFlowTextureAsset = new pc.Asset("uFlowTexture", "texture", {

url: "http://p1.qhimgs3.com/t01a07e2120f6d6e227.png"

});

var uCardTextureAsset = new pc.Asset("uCardTexture", "texture", {

url: "http://p1.qhimgs3.com/t0106a31ec8b61c2cb5.png"

});

var uMaskTextureAsset = new pc.Asset("uMaskTexture", "texture", {

url: "http://p1.qhimgs3.com/t0163659206d15e5cb5.jpg"

});

var uMainTextureAsset = new pc.Asset("uMainTexture", "texture", {

url: "http://p1.qhimgs3.com/t01dbc7e56d6af1643d.png"

});

application.assets.add(uTextureAsset);

application.assets.add(uNormalTextureAsset);

application.assets.add(uFlowTextureAsset);

application.assets.add(uCardTextureAsset);

application.assets.add(uMaskTextureAsset);

application.assets.add(uMainTextureAsset);




plane.script.on('create:cardManShader', function (_shader) {


_shader.vs = vsglsl;

_shader.fs = fsglsl;

_shader.uTexture = uTextureAsset;

_shader.uNormalTexture = uNormalTextureAsset;

_shader.uFlowTexture = uFlowTextureAsset;

_shader.uCardTexture = uCardTextureAsset;

_shader.uMaskTexture = uMaskTextureAsset;

_shader.uMainTexture = uMainTextureAsset;

})

plane.script.create('cardManShader');

//shader



application.assets.load(uTextureAsset);

application.assets.load(uNormalTextureAsset);

application.assets.load(uFlowTextureAsset);

application.assets.load(uCardTextureAsset);

application.assets.load(uMaskTextureAsset);

application.assets.load(uMainTextureAsset);


application.assets.on("load",function(){

plane.script.cardManShader.updateShader();

})

application.root.addChild(plane);




//最后再开始tick  ,提前开始tick会在真机上有未查明的问题

application.start();

这样就能在小程序中展现我们动图中的特效了。


具体代码,稍后提交到github。请关注





结尾打个广告 下面是我们目前创业在做的小程序,大家进来玩下吧




最后一次编辑于  2019-05-30  
点赞 3
收藏
评论

4 个评论

  • J.C
    J.C
    2019-10-29

    image.instanceof = 'HTMLImageElement';

    ... 还能这样写的么

    2019-10-29
    赞同
    回复
  • 大大
    大大
    2019-08-12

    大佬是创业失败了吗?小程序用不了啊

    2019-08-12
    赞同
    回复
  • Ameng
    Ameng
    2019-07-28

    github没有更新呀,小程序点进去也什么都没有,是不维护了么

    2019-07-28
    赞同
    回复
  • 小程序技术专员-拉风
    小程序技术专员-拉风
    2019-05-30

    代码格式太乱了

    2019-05-30
    赞同
    回复 2
    • 一步一脚印
      一步一脚印
      2019-06-03

      过分,你太直接了啊

      2019-06-03
      回复
    • 麻辣烫不烫จุ๊บ
      麻辣烫不烫จุ๊บ
      2019-09-02
      太过分了,说的真直接
      2019-09-02
      回复
登录 后发表内容