一、模型光源
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;
})