乐趣区

关于webgl:Cesium-实现建筑夜景贴图

在线预览
Demo 源码

在新版本的 Cesium(1.87.0),反对了 CustomShader,能够为 Cesium3DTileset 编写自定义的 GLSL 代码。在此之前,要批改 3D Tileset 的款式,只能通过 style 属性,能做的最多也就是依据高度使不同修建展现不同色彩这种水平。

CustomShader 的根底用法能够查看官网文档,这里不再赘述,次要是介绍如何利用 CustomShader 实现修建贴图。

楼顶着色

首先,楼顶不会和周围贴一样的图,先对立解决成深色。

并没有间接的属性表明以后点位于哪块墙上,但浏览文档能够发现,Cesium 提供了 normalMC 属性,也就是这个点所在立体的单位法向量。而楼顶个别是于高空平行的,将其法向量与高空的法向量比拟,如果夹角较小,就能够把它看作楼顶。

FragmentShader 代码如下(normalMC 在片段着色器不可用,须要在顶点着色器中通过 varying 传递过去,这里省略了过程):

void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {if (dot(vec3(0.0, 0.0, 1.0), v_normalMC) > 0.95) {material.diffuse = vec3(0.079, 0.107, 0.111);
  }
}

对两个向量做 dot(点积),能够失去这两个向量夹角的余弦值,再做反余弦,即可失去夹角,但反余弦操作比较慢,并且这里不须要准确的夹角值,所以对余弦值的大小做判断就行了

楼顶着色残缺代码

周围贴图

解决完楼顶,再解决四个侧面。贴图的要害是确定一个二维坐标,而后间接调用 texture2D 办法获取图片上对应色彩。从视觉上看,Shader 中的 Z 坐标必定是对应图片的纵坐标,问题在于 Shader 中的 X,Y 坐标如何对应到图片的横坐标

其实这里不能应用繁多的 X,Y 坐标,这样做会有很多修建的某个侧面是繁多的色彩,达不到贴图的成果。我最初还是利用了法向量属性,计算他们与 vec3(1.0, 0.0, 0.0) & vec3(0.0, 1.0, 0.0) 的夹角,如果与 vec3(1.0, 0.0, 0.0) 的夹角较小,就应用它的 Y 坐标,反之亦然。

修建 Shader 代码:

const createBuildingShader = () => {
  return new Cesium.CustomShader({
    lightingModel: Cesium.LightingModel.UNLIT,
    varyings: {v_normalMC: Cesium.VaryingType.VEC3},
    uniforms: {
      u_texture: {
        value: new Cesium.TextureUniform({url: '/demos/buildings/wall.png'}),
        type: Cesium.UniformType.SAMPLER_2D
      }
    },
    vertexShaderText: `
void vertexMain(VertexInput vsInput, inout vec3 positionMC) {v_normalMC = vsInput.attributes.normalMC;}`,
    fragmentShaderText: /* 见下 */ ``
  })
}

片段着色器代码:

void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {
  vec3 positionMC = fsInput.attributes.positionMC;
  float width = 75.0;
  float height = 75.0;
  if (dot(vec3(0.0, 0.0, 1.0), v_normalMC) > 0.95) {material.diffuse = vec3(0.079, 0.107, 0.111);
  } else {
    float textureX = 0.0;
    float dotYAxis = dot(vec3(0.0, 1.0, 0.0), v_normalMC);
    // cos(45deg) 约等于 0.71
    if (dotYAxis > 0.71 || dotYAxis < -0.71) {textureX = mod(positionMC.x, width) / width;
    } else {textureX = mod(positionMC.y, width) / width;
    }

    float textureY = mod(positionMC.z, height) / height;
    vec3 rgb = texture2D(u_texture, vec2(textureX, textureY)).rgb;
    material.diffuse = rgb;
  }
}

最初能够加上一些路线流光,使城市显得更加古代,就是文章结尾的成果了。

退出移动版