Cesium高阶学习十一、模型特效

一、模型光源
1、关于光照原理可以参考“WebGL编程指南” 第八章
在这里插入图片描述

2、以点光源为例,计算公式为 最后的颜色=光源颜色 x 物体基底色 x( 光线方向 . 物体法线 )
在这里插入图片描述

3、Cesium中如何实现

在模型上实现光源效果,我们可以使用 CustomShader 类,来实现,因为我们已经有了计算公式,只需要按照公式获取相关参数即可实现。计算公式: 最后的颜色=光源颜色 x 物体基底色 x( 光线方向 . 物体法线 )(关于如何传递参数到着色器中,可以参考“进阶学习”模型压平章节)
光源颜色:光源颜色肯定是我们从外部传递到着色器内部的
光线方向:光线方向是通过光源位置和片元位置计算而来,而光源位置是从外部传递到着色器内部
物体基底色:即物体原本的颜色,在着色器中通过material.diffuse获取
物体法线:在着色器中可以通过fsInput.attributes.normalEC获取

CustomShader类的片元着色器默认能够获取到的相关参数在文档中看不到,可以根据Cesium提供的示例或者查看源码获取更多的参数

接下来我们创建一个CustomShader类进行测试

a、首先加载一个模型(这里加载一个白膜数据)
let url = 'http://xt3d.online:8081/data/country_build/chengdu_ajust_clip/tileset.json';
Cesium.Cesium3DTileset.fromUrl(url, {
}).then((result) => {
    viewer.scene.primitives.add(result);
    viewer.zoomTo(result);
}).catch((err) => {
});

在这里插入图片描述

b、然后创建一个CustomShader,并且将光源位置和颜色作为参数传递到着色器中
let linghtPosition=Cesium.Cartesian3.fromDegrees(104.06722494841634, 30.63871487729699, 200);
 let linghtColor=Cesium.Color.RED;
 tileset.customShader = new Cesium.CustomShader({
       lightingModel: Cesium.LightingModel.UNLIT,
       uniforms: {
           u_lightPosition: {
               type: Cesium.UniformType.VEC3,
               value: linghtPosition
           },

           u_lightColor: {
               type: Cesium.UniformType.VEC3,
               value: linghtColor
           },
       },
       fragmentShaderText: ` 
           void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
               //当前片元的模型坐标
               vec3 positionMC = fsInput.attributes.positionMC;
               //当前片元的眼睛坐标
               vec3 positionEC = fsInput.attributes.positionEC;
               //当前片元的世界坐标
               vec3 positionWC=(czm_model * vec4(positionMC,1.0)).xyz;
               //当前片元的法线(眼睛坐标系)
               vec3 normalEC = fsInput.attributes.normalEC;
               normalEC=normalize(normalEC);
               //光线方向(眼睛坐标系)
               vec3 lightDirection = (czm_view * vec4(u_lightPosition,1.0)).xyz - positionEC;
           }
           `
   })
c、接下来根据公司计算颜色
fragmentShaderText: ` 
 void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {                 

     //当前片元的模型坐标
     vec3 positionMC = fsInput.attributes.positionMC;
     //当前片元的眼睛坐标
     vec3 positionEC = fsInput.attributes.positionEC;
     //当前片元的模型坐标
     vec3 positionWC=(czm_model * vec4(positionMC,1.0)).xyz;
     //当前片元的法线(眼睛坐标系)
     vec3 normalEC = fsInput.attributes.normalEC;
     normalEC=normalize(normalEC);
     //光线方向(眼睛坐标)
     vec3 lightDirection = (czm_view * vec4(u_lightPosition,1.0)).xyz - positionEC;                    
     //光线与法线的夹角(眼睛坐标)
     float a=dot(normalEC,normalize(lightDirection));
     material.diffuse*=a*u_lightColor; 
 }
 `

在这里插入图片描述

4、添加限制条件

	我们通过公式实现了光源的效果,但是因为没有添加条件限制,所以光源范围很多,这并不不符合现实情况,本节我们为光源添加条件限制。因为我们实现的是点光源,所以可以添加一个半径作为限制条件,即圆心越远,光越弱。
tileset.customShader = new Cesium.CustomShader({
     // mode: Cesium.CustomShaderMode.REPLACE_MATERIAL,
     lightingModel: Cesium.LightingModel.UNLIT,
     uniforms: {
         u_lightPosition: {
             type: Cesium.UniformType.VEC3,
             value: linghtPosition
         },

         u_lightColor: {
             type: Cesium.UniformType.VEC3,
             value: linghtColor
         },

         u_lightRadius:{
             type: Cesium.UniformType.FLOAT,
             value: 1000
         },
     },
     fragmentShaderText: ` 
         void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {                 

             //当前片元的模型坐标
             vec3 positionMC = fsInput.attributes.positionMC;
             //当前片元的眼睛坐标
             vec3 positionEC = fsInput.attributes.positionEC;
             //当前片元的模型坐标
             vec3 positionWC=(czm_model * vec4(positionMC,1.0)).xyz;
             //当前片元的法线(眼睛坐标系)
             vec3 normalEC = fsInput.attributes.normalEC;
             normalEC=normalize(normalEC);
             //光线方向(眼睛坐标)
             vec3 lightDirection = (czm_view * vec4(u_lightPosition,1.0)).xyz - positionEC;                    
             //光线于法线的夹角(眼睛坐标)
             float a=dot(normalEC,normalize(lightDirection));
             //material.diffuse*=a*u_lightColor;

             //衰减
             float distance=length(lightDirection);
             float b=1.-distance/u_lightRadius;
             b=max(b, 0.);
             if(b > 0.00001 ) {
                 //叠加为颜色值 
                 material.diffuse*=a*u_lightColor*b;
             } else{
                 material.diffuse=vec3(0.,0.,0.);
             }
         }
         `
 })

在这里插入图片描述

5、多光源

实现多光源,只需要将多个光源的颜色进行叠加即可
tileset.customShader = new Cesium.CustomShader({
     // mode: Cesium.CustomShaderMode.REPLACE_MATERIAL,
     lightingModel: Cesium.LightingModel.UNLIT,
     uniforms: {
         u_lightPosition: {
             type: Cesium.UniformType.VEC3,
             value: linghtPosition
         },

         u_lightColor: {
             type: Cesium.UniformType.VEC3,
             value: linghtColor
         },

         u_lightPosition1: {
             type: Cesium.UniformType.VEC3,
             value: linghtPosition1
         },

         u_lightColor1: {
             type: Cesium.UniformType.VEC3,
             value: linghtColor1
         },

         u_lightPosition2: {
             type: Cesium.UniformType.VEC3,
             value: linghtPosition2
         },

         u_lightColor2: {
             type: Cesium.UniformType.VEC3,
             value: linghtColor2
         },

         u_lightRadius:{
             type: Cesium.UniformType.FLOAT,
             value: 1000
         },

         u_lightRadius1:{
             type: Cesium.UniformType.FLOAT,
             value: 1000
         },

         u_lightRadius2:{
             type: Cesium.UniformType.FLOAT,
             value: 1000
         },
     },
     fragmentShaderText: ` 
         void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {                 

             //当前片元的模型坐标
             vec3 positionMC = fsInput.attributes.positionMC;
             //当前片元的眼睛坐标
             vec3 positionEC = fsInput.attributes.positionEC;
             //当前片元的模型坐标
             vec3 positionWC=(czm_model * vec4(positionMC,1.0)).xyz;
             //当前片元的法线(眼睛坐标系)
             vec3 normalEC = fsInput.attributes.normalEC;
             normalEC=normalize(normalEC);

             vec3 finalColor=vec3(0.);
             //光线方向(眼睛坐标)
             vec3 lightDirection = (czm_view * vec4(u_lightPosition,1.0)).xyz - positionEC;                    
             //光线于法线的夹角(眼睛坐标)
             float a=dot(normalEC,normalize(lightDirection));

             //衰减
             float distance=length(lightDirection);
             float b=1.-distance/u_lightRadius;
             b=max(b, 0.);
             if(b > 0.00001 ) {
                 //叠加为颜色值 
                 finalColor=a*u_lightColor*b;
             } else{
                 finalColor=vec3(0.,0.,0.);
             } 

             //光线方向(眼睛坐标)
             lightDirection = (czm_view * vec4(u_lightPosition1,1.0)).xyz - positionEC;                    
             //光线于法线的夹角(眼睛坐标)
             a=dot(normalEC,normalize(lightDirection));


             //衰减
             distance=length(lightDirection);
             b=1.-distance/u_lightRadius1;
             b=max(b, 0.);
             if(b > 0.00001 ) {
                 //叠加为颜色值 
                 finalColor=  mix(  finalColor,  a*u_lightColor1*b ,  0.5  );
             } 


             //光线方向(眼睛坐标)
             lightDirection = (czm_view * vec4(u_lightPosition2,1.0)).xyz - positionEC;                    
             //光线于法线的夹角(眼睛坐标)
             a=dot(normalEC,normalize(lightDirection));


             //衰减
             distance=length(lightDirection);
             b=1.-distance/u_lightRadius1;
             b=max(b, 0.);
             if(b > 0.00001 ) {
                 //叠加为颜色值 
                 finalColor=  mix(  finalColor,  a*u_lightColor2*b ,  0.5  );
             } 

             material.diffuse*=finalColor;
         }
         `
 })

在这里插入图片描述

二、线框扫描效果

要实现线框扫描效果,一种简单的做法是加载两份数据,一份正常显示,一份设置线框模式显示,并且设置显示的范围

在这里插入图片描述

代码:

	 Cesium.Cesium3DTileset.fromUrl(url, {
     debugWireframe: true,
     enableDebugWireframe: true, 
 }).then((result) => {
    let building= viewer.scene.primitives.add(result); 
 })

在这里插入图片描述

设置指定范围内的模型显示,可以通过customShader属性在片元着色器中设置显示的范围

Cesium.Cesium3DTileset.fromUrl(url, {
     debugWireframe: true,
     enableDebugWireframe: true,
     customShader: new Cesium.CustomShader({
         mode: Cesium.CustomShaderMode.REPLACE_MATERIAL,
         lightingModel: Cesium.LightingModel.UNLIT,
         uniforms: {
             u_color: {
                 type: Cesium.UniformType.VEC4,
                 value: Cesium.Color.fromCssColorString("#62809b"),
             },
         },
         fragmentShaderText: ` 
             void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) { 
                 vec3 positionEC = fsInput.attributes.positionEC; 
                 if(positionEC.x >100.&&positionEC.x<200.  ){
                      material.diffuse = u_color.rgb;
                      material.alpha = 1.;
                 }else{
                     discard;
                 } 
             }
             `
     })
 })

在这里插入图片描述

让显示的范围动起来,可以通过变量来控制显示的范围

customShader: new Cesium.CustomShader({
    mode: Cesium.CustomShaderMode.REPLACE_MATERIAL,
    lightingModel: Cesium.LightingModel.UNLIT,
    uniforms: {
        u_range: {
            type: Cesium.UniformType.FLOAT,
            value: 0,
        },
        u_color: {
            type: Cesium.UniformType.VEC4, 
            value: Cesium.Color.fromCssColorString("#62809b"),
        },
    },
    fragmentShaderText: ` 
        void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) { 
            vec3 positionEC = fsInput.attributes.positionEC; 
            if(positionEC.x >u_range&&positionEC.x<u_range+400.  ){
                 material.diffuse = u_color.rgb;
                 material.alpha = 1.;
            }else{
                discard;
            } 
        }
        `
})

通过u_range变量控制显示的范围,需要不断改变范围的值

let value = 20;
 viewer.scene.postUpdate.addEventListener((scene, time) => { 
     if (building.customShader.uniforms.u_range.value < -5000) {
         value = 20;
     } else if (building.customShader.uniforms.u_range.value > 5000) {
         value = -20;
     }
     building.customShader.uniforms.u_range.value += value;
 })

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cesium进阶学习

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值