diff --git a/demos/07_city.html b/demos/07_city.html index 032dfe9bcc..0b5acd860f 100644 --- a/demos/07_city.html +++ b/demos/07_city.html @@ -32,35 +32,35 @@ const scene = new L7.Scene({ }); window.scene = scene; scene.on('loaded', () => { - $.get('https://gw.alipayobjects.com/os/rmsportal/XHMbjQwrSrajvLLvMPbK.json', data => { - scene.PolygonLayer({ - zIndex: 0 - }) - .source(data) - .shape('fill') - .active({fill:'blue'}) - .color('rgb(79,174,234)') - .render(); - }); - $.get('https://gw.alipayobjects.com/os/rmsportal/VifgwJEyBIXnDrjCwWdK.json', data => { - scene.PolygonLayer({ - zIndex: 0 - }) - .source(data) - .shape('fill') - .color('rgb(156,194,116)') - .render(); - }); - $.get('https://gw.alipayobjects.com/os/rmsportal/ZseLNWMOPGrgqQYfvtli.json', data => { - scene.LineLayer({ - zIndex: 2 - }) - .source(data) - .shape('line') - .size([3,0]) - .color('rgb(79,174,234)') - .render(); - }); + // $.get('https://gw.alipayobjects.com/os/rmsportal/XHMbjQwrSrajvLLvMPbK.json', data => { + // scene.PolygonLayer({ + // zIndex: 0 + // }) + // .source(data) + // .shape('fill') + // .active({fill:'blue'}) + // .color('rgb(79,174,234)') + // .render(); + // }); + // $.get('https://gw.alipayobjects.com/os/rmsportal/VifgwJEyBIXnDrjCwWdK.json', data => { + // scene.PolygonLayer({ + // zIndex: 0 + // }) + // .source(data) + // .shape('fill') + // .color('rgb(156,194,116)') + // .render(); + // }); + // $.get('https://gw.alipayobjects.com/os/rmsportal/ZseLNWMOPGrgqQYfvtli.json', data => { + // scene.LineLayer({ + // zIndex: 2 + // }) + // .source(data) + // .shape('line') + // .size([3,0]) + // .color('rgb(79,174,234)') + // .render(); + // }); $.get('https://gw.alipayobjects.com/os/rmsportal/ggFwDClGjjvpSMBIrcEx.json', data => { citylayer = scene.PolygonLayer({ @@ -69,6 +69,24 @@ scene.on('loaded', () => { .source(data) .shape('extrude') .active({fill:'red'}) + .style({ + lights: [ + { + type: 'directional', + direction: [ 1, 10.5, 12 ], + ambient: [ 0.2, 0.2, 0.2 ], + diffuse: 'red', + specular: [ 0.1, 0.1, 0.1 ] + }, + { + type: 'directional', + direction: [ 1, -10.5, 12 ], + ambient: [ 0.2, 0.2, 0.2 ], + diffuse: 'green', + specular: [ 0.1, 0.1, 0.1 ] + }, + ] + }) .size('floor',[10,2000]) .color('rgba(242,246,250,0.96)') .render(); diff --git a/demos/taxi.html b/demos/taxi.html index dc2edc621e..8b89708488 100644 --- a/demos/taxi.html +++ b/demos/taxi.html @@ -55,7 +55,23 @@ scene.on('loaded', () => { baseColor:'rgb(16,16,16)', windowColor:'rgb(30,60,89)', //brightColor:'rgb(155,217,255)' - brightColor:'rgb(255,176,38)' + brightColor:'rgb(255,176,38)', + lights: [ + { + type: 'directional', + direction: [ 1, 10.5, 12 ], + ambient: [ 0.2, 0.2, 0.2 ], + diffuse: 'red', + specular: [ 0.1, 0.1, 0.1 ] + }, + { + type: 'directional', + direction: [ 1, -10.5, 12 ], + ambient: [ 0.2, 0.2, 0.2 ], + diffuse: 'green', + specular: [ 0.1, 0.1, 0.1 ] + }, + ] }) .render(); diff --git a/src/geom/material/polygonMaterial.js b/src/geom/material/polygonMaterial.js index 5de4c72acc..d77069e5fa 100644 --- a/src/geom/material/polygonMaterial.js +++ b/src/geom/material/polygonMaterial.js @@ -1,33 +1,15 @@ import Material from './material'; -import { getModule } from '../../util/shaderModule'; -export default class PolygonMaterial extends Material { - getDefaultParameters() { - return { - uniforms: { - u_opacity: { value: 1.0 }, - u_time: { value: 0 }, - u_zoom: { value: 0 }, - u_baseColor: { value: [ 1.0, 0, 0, 1.0 ] }, - u_brightColor: { value: [ 1.0, 0, 0, 1.0 ] }, - u_windowColor: { value: [ 1.0, 0, 0, 1.0 ] }, - u_near: { value: 0.0 }, - u_far: { value: 1.0 }, - u_activeId: { value: 0 }, - u_activeColor: { value: [ 1.0, 0, 0, 1.0 ] } - }, - defines: { +import { getModule, wrapUniforms } from '../../util/shaderModule'; +import merge from '@antv/util/lib/deep-mix'; - } - }; - } +export default class PolygonMaterial extends Material { constructor(_uniforms, _defines, parameters) { super(parameters); - const { uniforms, defines } = this.getDefaultParameters(); - this.uniforms = Object.assign(uniforms, this.setUniform(_uniforms)); + const { vs, fs, uniforms } = getModule('polygon'); + this.uniforms = wrapUniforms(merge(uniforms, _uniforms)); this.type = 'PolygonMaterial'; - this.defines = Object.assign(defines, _defines); + this.defines = _defines; - const { vs, fs } = getModule('polygon'); this.vertexShader = vs; this.fragmentShader = fs; this.transparent = true; diff --git a/src/geom/shader/index.js b/src/geom/shader/index.js index 2b17a38656..2943ef4c5b 100644 --- a/src/geom/shader/index.js +++ b/src/geom/shader/index.js @@ -52,12 +52,14 @@ import common from './common.glsl'; import { registerModule } from '../../util/shaderModule'; import pick_color from './shaderChunks/pick_color.glsl'; import decode from './shaderChunks/decode.glsl'; +import lighting from './shaderChunks/lighting.glsl'; import sdf_2d from './shaderChunks/sdf_2d.glsl'; export function compileBuiltinModules() { registerModule('point', { vs: point_vert, fs: point_frag }); registerModule('common', { vs: common, fs: common }); registerModule('decode', { vs: decode, fs: '' }); + registerModule('lighting', { vs: lighting, fs: '' }); registerModule('sdf_2d', { vs: '', fs: sdf_2d }); registerModule('pick_color', { vs: pick_color, fs: pick_color }); registerModule('circle', { vs: circle_vert, fs: circle_frag }); diff --git a/src/geom/shader/polygon_frag.glsl b/src/geom/shader/polygon_frag.glsl index 545721cd04..8f1780ce9a 100644 --- a/src/geom/shader/polygon_frag.glsl +++ b/src/geom/shader/polygon_frag.glsl @@ -1,16 +1,18 @@ precision highp float; -uniform sampler2D u_texture; -uniform vec4 u_baseColor; -uniform vec4 u_brightColor; -uniform vec4 u_windowColor; -uniform float u_zoom; -uniform float u_time; -uniform float u_near; -uniform float u_far; + +uniform vec4 u_baseColor : [ 1.0, 0, 0, 1.0 ]; +uniform vec4 u_brightColor : [ 1.0, 0, 0, 1.0 ]; +uniform vec4 u_windowColor : [ 1.0, 0, 0, 1.0 ]; +uniform float u_zoom : 0; +uniform float u_time : 0; +uniform float u_near : 0; +uniform float u_far : 1; + +#ifdef ANIMATE varying vec2 v_texCoord; -varying vec4 v_color; -varying float v_lightWeight; -varying float v_size; +#endif + +varying vec4 v_color; varying vec4 worldId; vec3 getWindowColor(float n, float hot, vec3 brightColor, vec3 darkColor) { @@ -41,12 +43,6 @@ float sdRect(vec2 p, vec2 sz) { float inside = min(max(d.x, d.y), 0.); return outside + inside; } -float circle(in vec2 _st, in float _radius){ - vec2 dist = _st-vec2(0.5); - return 1.-smoothstep(_radius-(_radius*0.01), - _radius+(_radius*0.01), - dot(dist,dist)*4.0); -} void main() { if(v_color.w == 0.0) { @@ -104,19 +100,13 @@ void main() { // if(head ==1.0) { // 顶部亮线 // color = brightColor; // } - color = color * v_lightWeight; + color = color * v_color.rgb; vec3 foggedColor = fog(color,fogColor,depth); gl_FragColor = vec4(foggedColor,1.0); } #else - // #ifdef SHAPE - // vec2 st = gl_FragCoord.xy / v_size ; - // vec3 color = vec3(circle(st,0.5)); - // gl_FragColor = vec4(color, 1.0 ); - // return; - // #endif gl_FragColor = vec4(v_color.xyz , v_color.w); #endif diff --git a/src/geom/shader/polygon_vert.glsl b/src/geom/shader/polygon_vert.glsl index 815244fb97..ad811a1c7b 100644 --- a/src/geom/shader/polygon_vert.glsl +++ b/src/geom/shader/polygon_vert.glsl @@ -1,61 +1,50 @@ precision highp float; -#define ambientRatio 0.5 -#define diffuseRatio 0.4 -#define specularRatio 0.1 -attribute vec4 a_color; -attribute vec2 faceUv; -attribute vec3 a_shape; -attribute vec3 a_size; -uniform float u_zoom; -uniform float u_opacity; -varying vec2 v_texCoord; -varying vec4 v_color; -varying float v_lightWeight; -varying float v_size; -uniform float u_activeId; -uniform vec4 u_activeColor; -void main() { - float scale = pow(2.0,(20.0 - u_zoom)); - mat4 matModelViewProjection = projectionMatrix * modelViewMatrix; - worldId = id_toPickColor(pickingId); - vec3 newposition = position; - // newposition.x -= 128.0; - #ifdef SHAPE - newposition =position + a_size * scale* a_shape + vec3(0., a_size.y * scale / 4., 0.); - #endif - v_texCoord = faceUv; - if(normal == vec3(0.,0.,1.)){ - v_color = a_color; - v_color.a *= u_opacity; - if(pickingId == u_activeId) { - v_color = u_activeColor; - } - v_size = a_size.x * scale; - gl_Position = matModelViewProjection * vec4(newposition, 1.0); - return; - } - - vec3 worldPos = vec3(vec4(newposition,1.0) * modelMatrix); - vec3 worldNormal = vec3(vec4(normal,1.0) * modelMatrix); - // //cal light weight - vec3 viewDir = normalize(cameraPosition - worldPos); - //vec3 lightDir = normalize(vec3(1, -10.5, 12)); - vec3 lightDir = normalize(vec3(0.,-10.,1.)); - vec3 halfDir = normalize(viewDir+lightDir); - // //lambert - float lambert = dot(worldNormal, lightDir); - //specular - float specular = pow( max(0.0, dot(worldNormal, halfDir)), 32.0); - //sum to light weight - float lightWeight = ambientRatio + diffuseRatio * lambert + specularRatio * specular; - v_texCoord = faceUv; - v_lightWeight = lightWeight; - - v_color =vec4(a_color.rgb*lightWeight, a_color.w); - if(pickingId == u_activeId) { - v_color = u_activeColor; - } - gl_Position = matModelViewProjection * vec4(newposition, 1.0); - +attribute vec4 a_color; + +#ifdef SHAPE +attribute vec3 a_size; +attribute vec3 a_shape; +#endif + +#ifdef ANIMATE +attribute vec2 faceUv; +varying vec2 v_texCoord; +#endif + +varying vec4 v_color; + +uniform float u_zoom : 0; +uniform float u_opacity : 1.0; +uniform float u_activeId : 0; +uniform vec4 u_activeColor : [1.0, 0.0, 0.0, 1.0]; + +#pragma include "lighting" + +void main() { + #ifdef ANIMATE + v_texCoord = faceUv; + #endif + v_color = a_color; + v_color.a *= u_opacity; + + // put offset in world space & shrink with current zoom level + float scale = pow(2.0,(20.0 - u_zoom)); + vec3 offset = vec3(0.); + #ifdef SHAPE + offset = vec3(a_size * scale * a_shape); + #endif + gl_Position = projectionMatrix * modelViewMatrix * vec4(position + offset, 1.); + + #ifdef LIGHTING + if (normal != vec3(0., 0., 1.)) { + vec3 viewDir = normalize(cameraPosition - position); + v_color.rgb *= calc_lighting(position, normal, viewDir); + } + #endif + + if(pickingId == u_activeId) { + v_color = u_activeColor; + } + worldId = id_toPickColor(pickingId); } \ No newline at end of file diff --git a/src/geom/shader/shaderChunks/lighting.glsl b/src/geom/shader/shaderChunks/lighting.glsl new file mode 100644 index 0000000000..330e8b5525 --- /dev/null +++ b/src/geom/shader/shaderChunks/lighting.glsl @@ -0,0 +1,100 @@ +// Blinn-Phong model +// apply lighting in vertex shader instead of fragment shader +// @see https://learnopengl.com/Advanced-Lighting/Advanced-Lighting +// TODO: support point light、spot light & sun light +uniform float u_ambient : 1.0; +uniform float u_diffuse : 1.0; +uniform float u_specular : 1.0; +uniform int u_num_of_directional_lights : 1; +uniform int u_num_of_spot_lights : 0; + +#define SHININESS 32.0 +#define MAX_NUM_OF_DIRECTIONAL_LIGHTS 3 +#define MAX_NUM_OF_SPOT_LIGHTS 3 + +#pragma include "common" + +struct DirectionalLight { + vec3 direction; + vec3 ambient; + vec3 diffuse; + vec3 specular; +}; + +struct SpotLight { + vec3 position; + vec3 direction; + vec3 ambient; + vec3 diffuse; + vec3 specular; + float constant; + float linear; + float quadratic; + float angle; + float blur; + float exponent; +}; + +uniform DirectionalLight u_directional_lights[MAX_NUM_OF_DIRECTIONAL_LIGHTS]; +uniform SpotLight u_spot_lights[MAX_NUM_OF_SPOT_LIGHTS]; + +vec3 calc_directional_light(DirectionalLight light, vec3 normal, vec3 viewDir) { + vec3 lightDir = normalize(light.direction); + // diffuse shading + float diff = max(dot(normal, lightDir), 0.0); + // Blinn-Phong specular shading + vec3 halfwayDir = normalize(lightDir + viewDir); + float spec = pow(max(dot(normal, halfwayDir), 0.0), SHININESS); + + vec3 ambient = light.ambient * u_ambient; + vec3 diffuse = light.diffuse * diff * u_diffuse; + vec3 specular = light.specular * spec * u_specular; + + return ambient + diffuse + specular; +} + +// vec3 calc_spot_light(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir) { +// vec3 lightDir = normalize(light.position - fragPos); +// // diffuse shading +// float diff = max(dot(normal, lightDir), 0.0); +// // specular shading +// vec3 reflectDir = reflect(-lightDir, normal); +// float spec = pow(max(dot(viewDir, reflectDir), 0.0), SHININESS); +// // attenuation +// float distance = length(light.position - fragPos); +// float attenuation = 1.0 / (light.constant + light.linear * distance + +// light.quadratic * (distance * distance)); + +// vec3 ambient = light.ambient * u_ambient; +// vec3 diffuse = light.diffuse * diff * u_diffuse; +// vec3 specular = light.specular * spec * u_specular; + +// float spotEffect = dot(normalize(light.direction), -lightDir); +// float spotCosCutoff = cos(light.angle / 180.0 * PI); +// float spotCosOuterCutoff = cos((light.angle + light.blur) / 180.0 * PI); +// float spotCosInnerCutoff = cos((light.angle - light.blur) / 180.0 * PI); +// if (spotEffect > spotCosCutoff) { +// spotEffect = pow(smoothstep(spotCosOuterCutoff, spotCosInnerCutoff, spotEffect), light.exponent); +// } else { +// spotEffect = 0.0; +// } + +// return ambient + attenuation * (spotEffect * diffuse + specular); +// } + +vec3 calc_lighting(vec3 position, vec3 normal, vec3 viewDir) { + vec3 weight = vec3(0.0); + for (int i = 0; i < MAX_NUM_OF_DIRECTIONAL_LIGHTS; i++) { + if (i >= u_num_of_directional_lights) { + break; + } + weight += calc_directional_light(u_directional_lights[i], normal, viewDir); + } + // for (int i = 0; i < MAX_NUM_OF_SPOT_LIGHTS; i++) { + // if (i >= u_num_of_spot_lights) { + // break; + // } + // weight += calc_spot_light(u_spot_lights[i], normal, position, viewDir); + // } + return weight; +} \ No newline at end of file diff --git a/src/layer/render/point/drawFill.js b/src/layer/render/point/drawFill.js index a6ec26dc49..cc2fcf741e 100644 --- a/src/layer/render/point/drawFill.js +++ b/src/layer/render/point/drawFill.js @@ -9,6 +9,7 @@ import * as THREE from '../../../core/three'; import * as PointBuffer from '../../../geom/buffer/point/index'; import DrawStroke from './drawStroke'; import PolygonMaterial from '../../../geom/material/polygonMaterial'; +import { generateLightingUniforms } from '../../../util/shaderModule'; export default function DrawFill(layerData, layer) { const style = layer.get('styleOptions'); const activeOption = layer.get('activedOptions'); @@ -34,9 +35,11 @@ export default function DrawFill(layerData, layer) { const material = new PolygonMaterial({ u_opacity: style.opacity, u_activeColor: activeOption.fill, - u_zoom: layer.scene.getZoom() + u_zoom: layer.scene.getZoom(), + ...generateLightingUniforms(style.lights) }, { - SHAPE: true + SHAPE: true, + LIGHTING: true }); material.setDefinesvalue('SHAPE', true); material.depthTest = false; diff --git a/src/layer/render/polygon/drawAnimate.js b/src/layer/render/polygon/drawAnimate.js index b8eed2ab34..beae138076 100644 --- a/src/layer/render/polygon/drawAnimate.js +++ b/src/layer/render/polygon/drawAnimate.js @@ -1,6 +1,8 @@ import * as THREE from '../../../core/three'; import PolygonBuffer from '../../../geom/buffer/polygon'; import PolygonMaterial from '../../../geom/material/polygonMaterial'; +import { generateLightingUniforms } from '../../../util/shaderModule'; + export default function DrawAnimate(layerData, layer) { const style = layer.get('styleOptions'); const { near, far } = layer.map.getCameraState(); @@ -9,7 +11,7 @@ export default function DrawAnimate(layerData, layer) { shape: 'extrude', layerData }); - const { opacity, baseColor, brightColor, windowColor } = style; + const { opacity, baseColor, brightColor, windowColor, lights } = style; const geometry = new THREE.BufferGeometry(); geometry.addAttribute('position', new THREE.Float32BufferAttribute(attributes.vertices, 3)); geometry.addAttribute('a_color', new THREE.Float32BufferAttribute(attributes.colors, 4)); @@ -23,9 +25,11 @@ export default function DrawAnimate(layerData, layer) { u_brightColor: brightColor, u_windowColor: windowColor, u_near: near, - u_far: far + u_far: far, + ...generateLightingUniforms(lights) }, { SHAPE: false, + LIGHTING: true, ANIMATE: true }); const fillPolygonMesh = new THREE.Mesh(geometry, material); diff --git a/src/layer/render/polygon/drawFill.js b/src/layer/render/polygon/drawFill.js index 11ae385a2e..6c32906c44 100644 --- a/src/layer/render/polygon/drawFill.js +++ b/src/layer/render/polygon/drawFill.js @@ -1,6 +1,7 @@ import * as THREE from '../../../core/three'; import PolygonBuffer from '../../../geom/buffer/polygon'; import PolygonMaterial from '../../../geom/material/polygonMaterial'; +import { generateLightingUniforms } from '../../../util/shaderModule'; export default function DrawPolygonFill(layerData, layer) { const style = layer.get('styleOptions'); @@ -9,7 +10,7 @@ export default function DrawPolygonFill(layerData, layer) { ...style, activeColor: activeOption.fill }; - const { opacity, activeColor } = config; + const { opacity, activeColor, lights } = config; const { attributes } = new PolygonBuffer({ shape: layer.shape, layerData @@ -21,9 +22,11 @@ export default function DrawPolygonFill(layerData, layer) { geometry.addAttribute('normal', new THREE.Float32BufferAttribute(attributes.normals, 3)); const material = new PolygonMaterial({ u_opacity: opacity, - u_activeColor: activeColor + u_activeColor: activeColor, + ...generateLightingUniforms(lights) }, { - SHAPE: false + SHAPE: false, + LIGHTING: true }); const fillPolygonMesh = new THREE.Mesh(geometry, material); return fillPolygonMesh; diff --git a/src/util/shaderModule.js b/src/util/shaderModule.js index 2c6c775d85..a4078be0bd 100644 --- a/src/util/shaderModule.js +++ b/src/util/shaderModule.js @@ -178,7 +178,8 @@ export function wrapUniforms(uniforms) { const DEFAULT_LIGHT = { type: 'directional', - direction: [ 1, 10.5, 12 ], + // direction: [ 1, 10.5, 12 ], + direction: [ 0, -10.5, 1 ], ambient: [ 0.2, 0.2, 0.2 ], diffuse: [ 0.6, 0.6, 0.6 ], specular: [ 0.1, 0.1, 0.1 ]