Shadow for buildings (#668)
Co-authored-by: Izumi Kawashima <schedul.xor@gmail.com>
This commit is contained in:
parent
ba123910a4
commit
b4a98ef1a9
@ -13,15 +13,31 @@ varying vec4 color;
|
|||||||
//varying float depth;
|
//varying float depth;
|
||||||
const float ff = 255.0;
|
const float ff = 255.0;
|
||||||
|
|
||||||
|
#ifdef SHADOW
|
||||||
|
uniform mat4 u_light_mvp;
|
||||||
|
varying vec4 v_shadow_coords;
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The diffuse of surface dependent on the light position.
|
* The diffuse of surface dependent on the light position.
|
||||||
*
|
*
|
||||||
* @param r_norm the normal vector of vertex's face
|
* @param r_norm the normal vector of vertex's face
|
||||||
*/
|
*/
|
||||||
float diffuse(in vec3 r_norm) {
|
float diffuse(in vec3 r_norm, out bool hasLight) {
|
||||||
float l = dot(normalize(r_norm), normalize(u_light));
|
float l = dot(normalize(r_norm), normalize(u_light));
|
||||||
|
hasLight = l > 0.0;
|
||||||
l = clamp((1.0 + l) / 2.0, 0.0, 1.0);
|
l = clamp((1.0 + l) / 2.0, 0.0, 1.0);
|
||||||
return(0.8 + l * 0.2);
|
#ifdef SHADOW
|
||||||
|
if (hasLight) {
|
||||||
|
//l = (l + (1.0 - r_norm.z)) * 0.5;
|
||||||
|
l = 0.8 + l * 0.2;
|
||||||
|
} else {
|
||||||
|
l = 0.5 + l * 0.3;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
l = 0.8 + l * 0.2;
|
||||||
|
#endif
|
||||||
|
return (l);
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
@ -31,6 +47,8 @@ void main() {
|
|||||||
height = u_zlimit;
|
height = u_zlimit;
|
||||||
}
|
}
|
||||||
gl_Position = u_mvp * vec4(a_pos.xy, height, 1.0);
|
gl_Position = u_mvp * vec4(a_pos.xy, height, 1.0);
|
||||||
|
bool hasLight = false;
|
||||||
|
|
||||||
//depth = gl_Position.z;
|
//depth = gl_Position.z;
|
||||||
if (u_mode == -1) {
|
if (u_mode == -1) {
|
||||||
;
|
;
|
||||||
@ -40,7 +58,7 @@ void main() {
|
|||||||
// roof / depth pass
|
// roof / depth pass
|
||||||
r_norm = vec3(0.0, 0.0, 1.0);
|
r_norm = vec3(0.0, 0.0, 1.0);
|
||||||
color = u_color[0] * u_alpha;
|
color = u_color[0] * u_alpha;
|
||||||
color.rgb *= diffuse(r_norm);
|
color.rgb *= diffuse(r_norm, hasLight);
|
||||||
} else {
|
} else {
|
||||||
float lightX = u_mode == 1 ? a_normal.y : a_normal.x;
|
float lightX = u_mode == 1 ? a_normal.y : a_normal.x;
|
||||||
r_norm.x = (lightX / ff) * 2.0 - 1.0;
|
r_norm.x = (lightX / ff) * 2.0 - 1.0;
|
||||||
@ -59,7 +77,7 @@ void main() {
|
|||||||
// sides 2 - use 0x00ff
|
// sides 2 - use 0x00ff
|
||||||
color = u_color[2];
|
color = u_color[2];
|
||||||
}
|
}
|
||||||
color.rgb *= diffuse(r_norm) * z * h;
|
color.rgb *= diffuse(r_norm, hasLight) * z * h;
|
||||||
}
|
}
|
||||||
color *= u_alpha;
|
color *= u_alpha;
|
||||||
} else if (u_mode == 3) {
|
} else if (u_mode == 3) {
|
||||||
@ -67,6 +85,15 @@ void main() {
|
|||||||
float z = (0.98 - gl_Position.z * 0.02);
|
float z = (0.98 - gl_Position.z * 0.02);
|
||||||
color = u_color[3] * z;
|
color = u_color[3] * z;
|
||||||
}
|
}
|
||||||
|
#ifdef SHADOW
|
||||||
|
if (hasLight) {
|
||||||
|
vec4 positionFromLight = u_light_mvp * a_pos;
|
||||||
|
v_shadow_coords = (positionFromLight / positionFromLight.w);
|
||||||
|
} else {
|
||||||
|
// Discard shadow on unlighted faces
|
||||||
|
v_shadow_coords = vec4(-1.0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
$$
|
$$
|
||||||
@ -76,6 +103,76 @@ precision highp float;
|
|||||||
#endif
|
#endif
|
||||||
varying vec4 color;
|
varying vec4 color;
|
||||||
|
|
||||||
void main() {
|
#ifdef SHADOW
|
||||||
gl_FragColor = color;
|
varying vec4 v_shadow_coords; // the coords in shadow map
|
||||||
|
|
||||||
|
uniform sampler2D u_shadowMap;
|
||||||
|
uniform vec4 u_lightColor;
|
||||||
|
uniform float u_shadowRes;
|
||||||
|
|
||||||
|
const bool DEBUG = false;
|
||||||
|
|
||||||
|
const float transitionDistance = 0.05; // relative transition distance at the border of shadow tex
|
||||||
|
const float minTrans = 1.0 - transitionDistance;
|
||||||
|
|
||||||
|
const int pcfCount = 2; // the number of surrounding pixels to smooth shadow
|
||||||
|
const float biasOffset = 0.005; // offset to remove shadow acne
|
||||||
|
const float pcfTexels = float((pcfCount * 2 + 1) * (pcfCount * 2 + 1));
|
||||||
|
|
||||||
|
#if GLVERSION == 20
|
||||||
|
float decodeFloat (vec4 color) {
|
||||||
|
const vec4 bitShift = vec4(
|
||||||
|
1.0 / (256.0 * 256.0 * 256.0),
|
||||||
|
1.0 / (256.0 * 256.0),
|
||||||
|
1.0 / 256.0,
|
||||||
|
1.0
|
||||||
|
);
|
||||||
|
return dot(color, bitShift);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
#ifdef SHADOW
|
||||||
|
float shadowX = abs((v_shadow_coords.x - 0.5) * 2.0);
|
||||||
|
float shadowY = abs((v_shadow_coords.y - 0.5) * 2.0);
|
||||||
|
if (shadowX > 1.0 || shadowY > 1.0) {
|
||||||
|
// Outside the light texture set to 0.0
|
||||||
|
gl_FragColor = vec4(color.rgb * u_lightColor.rgb, color.a);
|
||||||
|
if (DEBUG) {
|
||||||
|
gl_FragColor = vec4(0.0, 1.0, 0.0, 0.1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Inside set to 1.0; make a transition to the border
|
||||||
|
float shadowOpacity = (shadowX < minTrans && shadowY < minTrans) ? 1.0 :
|
||||||
|
(1.0 - (max(shadowX - minTrans, shadowY - minTrans) / transitionDistance));
|
||||||
|
float distanceToLight = clamp(v_shadow_coords.z - biasOffset, 0.0, 1.0); // avoid unexpected shadow
|
||||||
|
|
||||||
|
// Smooth shadow at borders
|
||||||
|
float shadowDiffuse = 0.0;
|
||||||
|
float texelSize = 1.0 / u_shadowRes;
|
||||||
|
for (int x = -pcfCount; x <= pcfCount; x++) {
|
||||||
|
for (int y = -pcfCount; y <= pcfCount; y++) {
|
||||||
|
#if GLVERSION == 30
|
||||||
|
float depth = texture2D(u_shadowMap, v_shadow_coords.xy + vec2(x, y) * texelSize).r;
|
||||||
|
#else
|
||||||
|
float depth = decodeFloat(texture2D(u_shadowMap, v_shadow_coords.xy + vec2(x, y) * texelSize));
|
||||||
|
#endif
|
||||||
|
if (distanceToLight > depth) {
|
||||||
|
shadowDiffuse += 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shadowDiffuse /= pcfTexels;
|
||||||
|
shadowDiffuse *= shadowOpacity;
|
||||||
|
|
||||||
|
if (DEBUG && shadowDiffuse < 1.0) {
|
||||||
|
gl_FragColor = vec4(shadowDiffuse, color.gb, 0.1);
|
||||||
|
} else {
|
||||||
|
gl_FragColor = vec4((color.rgb * u_lightColor.rgb) * (1.0 - u_lightColor.a * shadowDiffuse), color.a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
gl_FragColor = color;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,11 @@ attribute vec4 a_pos;
|
|||||||
attribute vec2 a_normal;
|
attribute vec2 a_normal;
|
||||||
varying vec4 color;
|
varying vec4 color;
|
||||||
|
|
||||||
|
#ifdef SHADOW
|
||||||
|
uniform mat4 u_light_mvp;
|
||||||
|
varying vec4 v_shadow_coords;
|
||||||
|
#endif
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
// change height by u_alpha
|
// change height by u_alpha
|
||||||
vec4 pos = a_pos;
|
vec4 pos = a_pos;
|
||||||
@ -28,18 +33,40 @@ void main() {
|
|||||||
|
|
||||||
float l = dot(r_norm, normalize(u_light));
|
float l = dot(r_norm, normalize(u_light));
|
||||||
|
|
||||||
//l *= 0.8
|
#ifdef SHADOW
|
||||||
|
bool hasLight = l > 0.0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//l *= 0.8;
|
||||||
//vec3 opp_light_dir = normalize(vec3(-u_light.xy, u_light.z));
|
//vec3 opp_light_dir = normalize(vec3(-u_light.xy, u_light.z));
|
||||||
//l += dot(r_norm, opp_light_dir) * 0.2;
|
//l += dot(r_norm, opp_light_dir) * 0.2;
|
||||||
|
|
||||||
// [-1,1] to range [0,1]
|
// [-1,1] to range [0,1]
|
||||||
l = (1.0 + l) / 2.0;
|
l = (1.0 + l) / 2.0;
|
||||||
|
|
||||||
|
#ifdef SHADOW
|
||||||
|
if (hasLight) {
|
||||||
l = 0.75 + l * 0.25;
|
l = 0.75 + l * 0.25;
|
||||||
|
} else {
|
||||||
|
l = 0.5 + l * 0.3;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
l = 0.75 + l * 0.25;
|
||||||
|
#endif
|
||||||
|
|
||||||
// extreme fake-ssao by height
|
// extreme fake-ssao by height
|
||||||
l += (clamp(a_pos.z / 2048.0, 0.0, 0.1) - 0.05);
|
l += (clamp(a_pos.z / 2048.0, 0.0, 0.1) - 0.05);
|
||||||
color = vec4(u_color.rgb * (clamp(l, 0.0, 1.0)), u_color.a) * u_alpha;
|
color = vec4(u_color.rgb * (clamp(l, 0.0, 1.0)), u_color.a) * u_alpha;
|
||||||
|
|
||||||
|
#ifdef SHADOW
|
||||||
|
if (hasLight) {
|
||||||
|
vec4 positionFromLight = u_light_mvp * a_pos;
|
||||||
|
v_shadow_coords = (positionFromLight / positionFromLight.w);
|
||||||
|
} else {
|
||||||
|
// Discard shadow on unlighted faces
|
||||||
|
v_shadow_coords = vec4(-1.0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
$$
|
$$
|
||||||
@ -49,6 +76,77 @@ precision highp float;
|
|||||||
#endif
|
#endif
|
||||||
varying vec4 color;
|
varying vec4 color;
|
||||||
|
|
||||||
void main() {
|
#ifdef SHADOW
|
||||||
gl_FragColor = color;
|
varying vec4 v_shadow_coords; // the coords in shadow map
|
||||||
|
|
||||||
|
uniform sampler2D u_shadowMap;
|
||||||
|
uniform vec4 u_lightColor;
|
||||||
|
uniform float u_shadowRes;
|
||||||
|
uniform int u_mode;
|
||||||
|
|
||||||
|
const bool DEBUG = false;
|
||||||
|
|
||||||
|
const float transitionDistance = 0.05; // relative transition distance at the border of shadow tex
|
||||||
|
const float minTrans = 1.0 - transitionDistance;
|
||||||
|
|
||||||
|
const int pcfCount = 2; // the number of surrounding pixels to smooth shadow
|
||||||
|
const float biasOffset = 0.005; // offset to remove shadow acne
|
||||||
|
const float pcfTexels = float((pcfCount * 2 + 1) * (pcfCount * 2 + 1));
|
||||||
|
|
||||||
|
#if GLVERSION == 20
|
||||||
|
float decodeFloat (vec4 color) {
|
||||||
|
const vec4 bitShift = vec4(
|
||||||
|
1.0 / (256.0 * 256.0 * 256.0),
|
||||||
|
1.0 / (256.0 * 256.0),
|
||||||
|
1.0 / 256.0,
|
||||||
|
1.0
|
||||||
|
);
|
||||||
|
return dot(color, bitShift);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
#ifdef SHADOW
|
||||||
|
float shadowX = abs((v_shadow_coords.x - 0.5) * 2.0);
|
||||||
|
float shadowY = abs((v_shadow_coords.y - 0.5) * 2.0);
|
||||||
|
if (shadowX > 1.0 || shadowY > 1.0) {
|
||||||
|
// Outside the light texture set to 0.0
|
||||||
|
gl_FragColor = vec4(color.rgb * u_lightColor.rgb, color.a);
|
||||||
|
if (DEBUG) {
|
||||||
|
gl_FragColor = vec4(0.0, 1.0, 0.0, 0.1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Inside set to 1.0; make a transition to the border
|
||||||
|
float shadowOpacity = (shadowX < minTrans && shadowY < minTrans) ? 1.0 :
|
||||||
|
(1.0 - (max(shadowX - minTrans, shadowY - minTrans) / transitionDistance));
|
||||||
|
float distanceToLight = clamp(v_shadow_coords.z - biasOffset, 0.0, 1.0); // avoid unexpected shadow
|
||||||
|
|
||||||
|
// smooth shadow at borders
|
||||||
|
float shadowDiffuse = 0.0;
|
||||||
|
float texelSize = 1.0 / u_shadowRes;
|
||||||
|
for (int x = -pcfCount; x <= pcfCount; x++) {
|
||||||
|
for (int y = -pcfCount; y <= pcfCount; y++) {
|
||||||
|
#if GLVERSION == 30
|
||||||
|
float depth = texture2D(u_shadowMap, v_shadow_coords.xy + vec2(x, y) * texelSize).r;
|
||||||
|
#else
|
||||||
|
float depth = decodeFloat(texture2D(u_shadowMap, v_shadow_coords.xy + vec2(x, y) * texelSize));
|
||||||
|
#endif
|
||||||
|
if (distanceToLight > depth) {
|
||||||
|
shadowDiffuse += 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shadowDiffuse /= pcfTexels;
|
||||||
|
shadowDiffuse *= shadowOpacity;
|
||||||
|
|
||||||
|
if (DEBUG && shadowDiffuse < 1.0) {
|
||||||
|
gl_FragColor = vec4(shadowDiffuse, color.gb, 0.1);
|
||||||
|
} else {
|
||||||
|
gl_FragColor = vec4((color.rgb * u_lightColor.rgb) * (1.0 - u_lightColor.a * shadowDiffuse), color.a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
gl_FragColor = color;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
91
vtm/resources/assets/shaders/extrusion_shadow_ground.glsl
Normal file
91
vtm/resources/assets/shaders/extrusion_shadow_ground.glsl
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#ifdef GLES
|
||||||
|
precision highp float;
|
||||||
|
#endif
|
||||||
|
attribute vec4 a_pos;
|
||||||
|
uniform mat4 u_mvp;
|
||||||
|
uniform mat4 u_light_mvp;
|
||||||
|
|
||||||
|
varying vec4 v_shadow_coords;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
gl_Position = u_mvp * a_pos;
|
||||||
|
vec4 positionFromLight = u_light_mvp * a_pos;
|
||||||
|
v_shadow_coords = (positionFromLight / positionFromLight.w);
|
||||||
|
}
|
||||||
|
|
||||||
|
$$
|
||||||
|
|
||||||
|
#ifdef GLES
|
||||||
|
precision highp float;
|
||||||
|
#endif
|
||||||
|
varying vec4 v_shadow_coords;
|
||||||
|
|
||||||
|
uniform sampler2D u_shadowMap;
|
||||||
|
uniform vec4 u_lightColor;
|
||||||
|
uniform float u_shadowRes;
|
||||||
|
|
||||||
|
const bool DEBUG = false;
|
||||||
|
|
||||||
|
const float transitionDistance = 0.05; // relative transition distance at the border of shadow tex
|
||||||
|
const float minTrans = 1.0 - transitionDistance;
|
||||||
|
|
||||||
|
const int pcfCount = 2; // the number of surrounding pixels to smooth shadow
|
||||||
|
const float biasOffset = 0.005; // offset to remove shadow acne
|
||||||
|
const float pcfTexels = float((pcfCount * 2 + 1) * (pcfCount * 2 + 1));
|
||||||
|
|
||||||
|
#if GLVERSION == 20
|
||||||
|
float decodeFloat (vec4 color) {
|
||||||
|
const vec4 bitShift = vec4(
|
||||||
|
1.0 / (256.0 * 256.0 * 256.0),
|
||||||
|
1.0 / (256.0 * 256.0),
|
||||||
|
1.0 / 256.0,
|
||||||
|
1.0
|
||||||
|
);
|
||||||
|
return dot(color, bitShift);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float shadowX = abs((v_shadow_coords.x - 0.5) * 2.0);
|
||||||
|
float shadowY = abs((v_shadow_coords.y - 0.5) * 2.0);
|
||||||
|
if (shadowX > 1.0 || shadowY > 1.0) {
|
||||||
|
// Outside the light texture set to 0.0
|
||||||
|
gl_FragColor = vec4(u_lightColor.rgb, 1.0);
|
||||||
|
if (DEBUG) {
|
||||||
|
gl_FragColor = vec4(0.0, 1.0, 0.0, 0.1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Inside set to 1.0; make a transition to the border
|
||||||
|
float shadowOpacity = (shadowX < minTrans && shadowY < minTrans) ? 1.0 :
|
||||||
|
(1.0 - (max(shadowX - minTrans, shadowY - minTrans) / transitionDistance));
|
||||||
|
#if GLVERSION == 30
|
||||||
|
float distanceToLight = v_shadow_coords.z; // remove shadow acne
|
||||||
|
#else
|
||||||
|
float distanceToLight = v_shadow_coords.z - biasOffset; // remove shadow acne
|
||||||
|
#endif
|
||||||
|
distanceToLight = clamp(distanceToLight, 0.0, 1.0); // avoid unexpected far shadow
|
||||||
|
// smooth shadow at borders
|
||||||
|
float shadowDiffuse = 0.0;
|
||||||
|
float texelSize = 1.0 / u_shadowRes;
|
||||||
|
for (int x = -pcfCount; x <= pcfCount; x++) {
|
||||||
|
for (int y = -pcfCount; y <= pcfCount; y++) {
|
||||||
|
#if GLVERSION == 30
|
||||||
|
float depth = texture2D(u_shadowMap, v_shadow_coords.xy + vec2(x, y) * texelSize).r;
|
||||||
|
#else
|
||||||
|
float depth = decodeFloat(texture2D(u_shadowMap, v_shadow_coords.xy + vec2(x, y) * texelSize));
|
||||||
|
#endif
|
||||||
|
if (distanceToLight > depth) {
|
||||||
|
shadowDiffuse += 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shadowDiffuse /= pcfTexels;
|
||||||
|
shadowDiffuse *= shadowOpacity;
|
||||||
|
|
||||||
|
if (DEBUG && shadowDiffuse < 1.0) {
|
||||||
|
gl_FragColor = vec4(shadowDiffuse, 0.0, 0.0, 0.1);
|
||||||
|
} else {
|
||||||
|
gl_FragColor = vec4(u_lightColor.rgb * (1.0 - u_lightColor.a * shadowDiffuse), 1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
53
vtm/resources/assets/shaders/extrusion_shadow_light.glsl
Normal file
53
vtm/resources/assets/shaders/extrusion_shadow_light.glsl
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#ifdef GLES
|
||||||
|
precision highp float;
|
||||||
|
precision highp int;
|
||||||
|
#endif
|
||||||
|
uniform mat4 u_mvp;
|
||||||
|
uniform vec4 u_color[4];
|
||||||
|
uniform int u_mode;
|
||||||
|
uniform float u_zlimit;
|
||||||
|
attribute vec4 a_pos;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
gl_Position = u_mvp * a_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
$$
|
||||||
|
|
||||||
|
#ifdef GLES
|
||||||
|
precision highp float;
|
||||||
|
precision highp int;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uniform float u_alpha;
|
||||||
|
|
||||||
|
#if GLVERSION == 20
|
||||||
|
vec4 encodeFloat (float depth) {
|
||||||
|
const vec4 bitShift = vec4(
|
||||||
|
256.0 * 256.0 * 256.0,
|
||||||
|
256.0 * 256.0,
|
||||||
|
256.0,
|
||||||
|
1.0
|
||||||
|
);
|
||||||
|
const vec4 bitMask = vec4(
|
||||||
|
0.0,
|
||||||
|
1.0 / 256.0,
|
||||||
|
1.0 / 256.0,
|
||||||
|
1.0 / 256.0
|
||||||
|
);
|
||||||
|
vec4 comp = fract(depth * bitShift);
|
||||||
|
comp -= comp.xxyz * bitMask;
|
||||||
|
return comp;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
if (u_alpha < 0.8)
|
||||||
|
discard; // remove shadow when alpha is too small
|
||||||
|
|
||||||
|
#if GLVERSION == 30
|
||||||
|
gl_FragColor = vec4(1.0);
|
||||||
|
#else
|
||||||
|
gl_FragColor = encodeFloat(gl_FragCoord.z);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
@ -2,7 +2,7 @@
|
|||||||
* Copyright 2013 Hannes Janetzek
|
* Copyright 2013 Hannes Janetzek
|
||||||
* Copyright 2016-2018 devemux86
|
* Copyright 2016-2018 devemux86
|
||||||
* Copyright 2016 Robin Boldt
|
* Copyright 2016 Robin Boldt
|
||||||
* Copyright 2017-2018 Gustl22
|
* Copyright 2017-2019 Gustl22
|
||||||
*
|
*
|
||||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||||
*
|
*
|
||||||
@ -32,6 +32,7 @@ import org.oscim.renderer.OffscreenRenderer;
|
|||||||
import org.oscim.renderer.OffscreenRenderer.Mode;
|
import org.oscim.renderer.OffscreenRenderer.Mode;
|
||||||
import org.oscim.renderer.bucket.ExtrusionBuckets;
|
import org.oscim.renderer.bucket.ExtrusionBuckets;
|
||||||
import org.oscim.renderer.bucket.RenderBuckets;
|
import org.oscim.renderer.bucket.RenderBuckets;
|
||||||
|
import org.oscim.renderer.light.ShadowRenderer;
|
||||||
import org.oscim.theme.IRenderTheme;
|
import org.oscim.theme.IRenderTheme;
|
||||||
import org.oscim.theme.styles.ExtrusionStyle;
|
import org.oscim.theme.styles.ExtrusionStyle;
|
||||||
import org.oscim.theme.styles.RenderStyle;
|
import org.oscim.theme.styles.RenderStyle;
|
||||||
@ -59,6 +60,11 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook, ZoomLim
|
|||||||
*/
|
*/
|
||||||
public static boolean RAW_DATA = false;
|
public static boolean RAW_DATA = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use shadow rendering.
|
||||||
|
*/
|
||||||
|
public static boolean SHADOW = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Let vanish extrusions / meshes which are covered by others.
|
* Let vanish extrusions / meshes which are covered by others.
|
||||||
* {@link org.oscim.renderer.bucket.RenderBucket#EXTRUSION}: roofs are always translucent.
|
* {@link org.oscim.renderer.bucket.RenderBucket#EXTRUSION}: roofs are always translucent.
|
||||||
@ -113,7 +119,10 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook, ZoomLim
|
|||||||
mZoomLimiter = new ZoomLimiter(tileLayer.getManager(), zoomMin, zoomMax, zoomMin);
|
mZoomLimiter = new ZoomLimiter(tileLayer.getManager(), zoomMin, zoomMax, zoomMin);
|
||||||
|
|
||||||
mRenderer = mExtrusionRenderer = new BuildingRenderer(tileLayer.tileRenderer(), mZoomLimiter, mesh, TRANSLUCENT);
|
mRenderer = mExtrusionRenderer = new BuildingRenderer(tileLayer.tileRenderer(), mZoomLimiter, mesh, TRANSLUCENT);
|
||||||
if (POST_AA)
|
// TODO Allow SHADOW and POST_AA at same time
|
||||||
|
if (SHADOW)
|
||||||
|
mRenderer = new ShadowRenderer(mExtrusionRenderer);
|
||||||
|
else if (POST_AA)
|
||||||
mRenderer = new OffscreenRenderer(Mode.SSAO_FXAA, mRenderer);
|
mRenderer = new OffscreenRenderer(Mode.SSAO_FXAA, mRenderer);
|
||||||
|
|
||||||
mRenderTheme = tileLayer.getTheme();
|
mRenderTheme = tileLayer.getTheme();
|
||||||
|
|||||||
@ -24,6 +24,7 @@ import org.oscim.core.Tile;
|
|||||||
import org.oscim.renderer.bucket.ExtrusionBucket;
|
import org.oscim.renderer.bucket.ExtrusionBucket;
|
||||||
import org.oscim.renderer.bucket.ExtrusionBuckets;
|
import org.oscim.renderer.bucket.ExtrusionBuckets;
|
||||||
import org.oscim.renderer.bucket.RenderBuckets;
|
import org.oscim.renderer.bucket.RenderBuckets;
|
||||||
|
import org.oscim.renderer.light.ShadowRenderer;
|
||||||
import org.oscim.renderer.light.Sun;
|
import org.oscim.renderer.light.Sun;
|
||||||
import org.oscim.utils.FastMath;
|
import org.oscim.utils.FastMath;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -49,6 +50,7 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
|
|||||||
|
|
||||||
private Sun mSun;
|
private Sun mSun;
|
||||||
private boolean mEnableCurrentSunPos;
|
private boolean mEnableCurrentSunPos;
|
||||||
|
private boolean mUseLight = true;
|
||||||
|
|
||||||
public ExtrusionRenderer(boolean mesh, boolean translucent) {
|
public ExtrusionRenderer(boolean mesh, boolean translucent) {
|
||||||
mMesh = mesh;
|
mMesh = mesh;
|
||||||
@ -106,7 +108,11 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
|
|||||||
int uZLimit;
|
int uZLimit;
|
||||||
|
|
||||||
public Shader(String shader) {
|
public Shader(String shader) {
|
||||||
if (!create(shader))
|
this(shader, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Shader(String shader, String directives) {
|
||||||
|
if (!createDirective(shader, directives))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uMVP = getUniform("u_mvp");
|
uMVP = getUniform("u_mvp");
|
||||||
@ -120,12 +126,20 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void enableCurrentSunPos(boolean enableSunPos) {
|
||||||
|
mEnableCurrentSunPos = enableSunPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Shader getShader() {
|
||||||
|
return mShader;
|
||||||
|
}
|
||||||
|
|
||||||
public Sun getSun() {
|
public Sun getSun() {
|
||||||
return mSun;
|
return mSun;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enableCurrentSunPos(boolean enableSunPos) {
|
public boolean isMesh() {
|
||||||
mEnableCurrentSunPos = enableSunPos;
|
return mMesh;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -209,6 +223,8 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
|
|||||||
gl.uniform1i(s.uMode, -1);
|
gl.uniform1i(s.uMode, -1);
|
||||||
|
|
||||||
for (int i = 0; i < mBucketsCnt; i++) {
|
for (int i = 0; i < mBucketsCnt; i++) {
|
||||||
|
if (ebs[i] == null)
|
||||||
|
return;
|
||||||
if (ebs[i].ibo == null)
|
if (ebs[i].ibo == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -232,7 +248,8 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
|
|||||||
gl.depthFunc(GL.EQUAL);
|
gl.depthFunc(GL.EQUAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
GLState.blend(true);
|
// Depth cannot be transparent (in GL20)
|
||||||
|
GLState.blend(mUseLight);
|
||||||
|
|
||||||
GLState.enableVertexArrays(s.aPos, s.aNormal);
|
GLState.enableVertexArrays(s.aPos, s.aNormal);
|
||||||
|
|
||||||
@ -266,6 +283,7 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
|
|||||||
gl.vertexAttribPointer(s.aPos, 3, GL.SHORT,
|
gl.vertexAttribPointer(s.aPos, 3, GL.SHORT,
|
||||||
false, RenderBuckets.SHORT_BYTES * 4, eb.getVertexOffset());
|
false, RenderBuckets.SHORT_BYTES * 4, eb.getVertexOffset());
|
||||||
|
|
||||||
|
if (mUseLight)
|
||||||
gl.vertexAttribPointer(s.aNormal, 2, GL.UNSIGNED_BYTE,
|
gl.vertexAttribPointer(s.aNormal, 2, GL.UNSIGNED_BYTE,
|
||||||
false, RenderBuckets.SHORT_BYTES * 4, eb.getVertexOffset() + RenderBuckets.SHORT_BYTES * 3);
|
false, RenderBuckets.SHORT_BYTES * 4, eb.getVertexOffset() + RenderBuckets.SHORT_BYTES * 3);
|
||||||
|
|
||||||
@ -319,7 +337,8 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* just a temporary reference! */
|
/* just a temporary reference! */
|
||||||
ebs[i] = null;
|
/* But for shadows we use them multiple times */
|
||||||
|
//ebs[i] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mTranslucent)
|
if (!mTranslucent)
|
||||||
@ -345,8 +364,16 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
|
|||||||
float x = (float) ((l.x - v.pos.x) * curScale);
|
float x = (float) ((l.x - v.pos.x) * curScale);
|
||||||
float y = (float) ((l.y - v.pos.y) * curScale);
|
float y = (float) ((l.y - v.pos.y) * curScale);
|
||||||
|
|
||||||
|
// Create model matrix
|
||||||
v.mvp.setTransScale(x, y, scale / COORD_SCALE);
|
v.mvp.setTransScale(x, y, scale / COORD_SCALE);
|
||||||
v.mvp.setValue(10, scale / 10);
|
v.mvp.setValue(10, scale / 10);
|
||||||
|
|
||||||
|
// Create shadow map converter
|
||||||
|
// TODO may code it cleaner
|
||||||
|
if (s instanceof ShadowRenderer.Shader)
|
||||||
|
((ShadowRenderer.Shader) s).setLightMVP(v.mvp);
|
||||||
|
|
||||||
|
// Apply model matrix to VP-Matrix
|
||||||
v.mvp.multiplyLhs(v.viewproj);
|
v.mvp.multiplyLhs(v.viewproj);
|
||||||
|
|
||||||
if (mTranslucent) {
|
if (mTranslucent) {
|
||||||
@ -359,7 +386,15 @@ public abstract class ExtrusionRenderer extends LayerRenderer {
|
|||||||
v.mvp.setAsUniform(s.uMVP);
|
v.mvp.setAsUniform(s.uMVP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setShader(Shader shader) {
|
||||||
|
mShader = shader;
|
||||||
|
}
|
||||||
|
|
||||||
public void setZLimit(float zLimit) {
|
public void setZLimit(float zLimit) {
|
||||||
mZLimit = zLimit;
|
mZLimit = zLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void useLight(boolean useLight) {
|
||||||
|
mUseLight = useLight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -195,7 +195,7 @@ public class OffscreenRenderer extends LayerRenderer {
|
|||||||
GLState.bindFramebuffer(fb);
|
GLState.bindFramebuffer(fb);
|
||||||
GLState.viewport(texW, texH);
|
GLState.viewport(texW, texH);
|
||||||
gl.depthMask(true);
|
gl.depthMask(true);
|
||||||
GLState.setClearColor(mClearColor);
|
GLState.setClearColor(mClearColor); // FIXME SHADOW remove to use default clear color
|
||||||
gl.clear(GL.DEPTH_BUFFER_BIT | GL.COLOR_BUFFER_BIT);
|
gl.clear(GL.DEPTH_BUFFER_BIT | GL.COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
mRenderer.render(viewport);
|
mRenderer.render(viewport);
|
||||||
@ -223,6 +223,7 @@ public class OffscreenRenderer extends LayerRenderer {
|
|||||||
|
|
||||||
GLState.test(false, false);
|
GLState.test(false, false);
|
||||||
GLState.blend(true);
|
GLState.blend(true);
|
||||||
|
// FIXME SHADOW to work with ShadowRenderer: gl.blendFunc(GL.ZERO, GL.SRC_COLOR);
|
||||||
gl.drawArrays(GL.TRIANGLE_STRIP, 0, 4);
|
gl.drawArrays(GL.TRIANGLE_STRIP, 0, 4);
|
||||||
GLUtils.checkGlError(getClass().getName() + ": render() end");
|
GLUtils.checkGlError(getClass().getName() + ": render() end");
|
||||||
}
|
}
|
||||||
|
|||||||
152
vtm/src/org/oscim/renderer/light/ShadowFrameBuffer.java
Normal file
152
vtm/src/org/oscim/renderer/light/ShadowFrameBuffer.java
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 Gustl22
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along with
|
||||||
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.oscim.renderer.light;
|
||||||
|
|
||||||
|
import org.oscim.backend.GL;
|
||||||
|
import org.oscim.backend.GLAdapter;
|
||||||
|
import org.oscim.renderer.GLState;
|
||||||
|
import org.oscim.renderer.GLUtils;
|
||||||
|
|
||||||
|
import static org.oscim.backend.GLAdapter.gl;
|
||||||
|
import static org.oscim.backend.GLAdapter.gl30;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The frame buffer for the shadow pass. This class sets up the depth texture
|
||||||
|
* which can be rendered to during the shadow render pass, producing a shadow
|
||||||
|
* map.
|
||||||
|
* <p>
|
||||||
|
* See ThinMatrix on Youtube: https://youtu.be/o6zDfDkOFIc
|
||||||
|
*/
|
||||||
|
public class ShadowFrameBuffer {
|
||||||
|
|
||||||
|
private final int WIDTH;
|
||||||
|
private final int HEIGHT;
|
||||||
|
private int defaultWidth;
|
||||||
|
private int defaultHeight;
|
||||||
|
private int defaultFrameBuffer;
|
||||||
|
private int defaultTexture;
|
||||||
|
private int fbo;
|
||||||
|
private int shadowMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialises the frame buffer and shadow map of a certain size.
|
||||||
|
*
|
||||||
|
* @param width - the width of the shadow map in pixels.
|
||||||
|
* @param height - the height of the shadow map in pixels.
|
||||||
|
*/
|
||||||
|
protected ShadowFrameBuffer(int width, int height) {
|
||||||
|
this.WIDTH = width;
|
||||||
|
this.HEIGHT = height;
|
||||||
|
|
||||||
|
updateViewportDimensions();
|
||||||
|
|
||||||
|
fbo = createFrameBuffer();
|
||||||
|
shadowMap = createDepthBufferAttachment(width, height);
|
||||||
|
unbindFrameBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the frame buffer and shadow map texture when the game closes.
|
||||||
|
*/
|
||||||
|
protected void cleanUp() {
|
||||||
|
GLUtils.glDeleteFrameBuffers(1, new int[]{fbo});
|
||||||
|
GLUtils.glDeleteTextures(1, new int[]{shadowMap});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unbinds the frame buffer, setting the default frame buffer as the current
|
||||||
|
* render target.
|
||||||
|
*/
|
||||||
|
protected void unbindFrameBuffer() {
|
||||||
|
GLState.bindFramebuffer(defaultFrameBuffer);
|
||||||
|
GLState.viewport(defaultWidth, defaultHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The ID of the shadow map texture.
|
||||||
|
*/
|
||||||
|
protected int getShadowMap() {
|
||||||
|
return shadowMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds the frame buffer as the current render target.
|
||||||
|
*/
|
||||||
|
public void bindFrameBuffer() {
|
||||||
|
updateViewportDimensions();
|
||||||
|
GLState.bindTex2D(defaultTexture);
|
||||||
|
|
||||||
|
defaultFrameBuffer = GLState.getFramebuffer();
|
||||||
|
GLState.bindFramebuffer(fbo);
|
||||||
|
GLState.viewport(WIDTH, HEIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a frame buffer and binds it so that attachments can be added to
|
||||||
|
* it. The draw buffer is set to none, indicating that there's no colour
|
||||||
|
* buffer to be rendered to.
|
||||||
|
*
|
||||||
|
* @return The newly created frame buffer's ID.
|
||||||
|
*/
|
||||||
|
private static int createFrameBuffer() {
|
||||||
|
int frameBuffer = GLUtils.glGenFrameBuffers(1)[0];
|
||||||
|
GLState.bindFramebuffer(frameBuffer);
|
||||||
|
if (GLAdapter.isGL30()) {
|
||||||
|
GLUtils.glDrawBuffers(1, new int[]{GL.NONE});
|
||||||
|
gl30.readBuffer(GL.NONE);
|
||||||
|
}
|
||||||
|
return frameBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a depth buffer texture attachment.
|
||||||
|
*
|
||||||
|
* @param width - the width of the texture.
|
||||||
|
* @param height - the height of the texture.
|
||||||
|
* @return The ID of the depth texture.
|
||||||
|
*/
|
||||||
|
private int createDepthBufferAttachment(int width, int height) {
|
||||||
|
defaultTexture = GLState.getTexture();
|
||||||
|
int[] texture = GLUtils.glGenTextures(1);
|
||||||
|
GLState.bindTex2D(texture[0]);
|
||||||
|
if (GLAdapter.isGL30()) {
|
||||||
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl30.DEPTH_COMPONENT16,
|
||||||
|
width, height, 0, gl30.DEPTH_COMPONENT,
|
||||||
|
gl.UNSIGNED_SHORT, null);
|
||||||
|
} else {
|
||||||
|
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,
|
||||||
|
width, height, 0, gl.RGBA,
|
||||||
|
gl.UNSIGNED_BYTE, null);
|
||||||
|
}
|
||||||
|
// Alternatively set to 32 bit float texture
|
||||||
|
// gl.texImage2D(gl.TEXTURE_2D, 0, gl30.DEPTH_COMPONENT32F, width, height, 0,
|
||||||
|
// gl.DEPTH_COMPONENT, gl.FLOAT, null);
|
||||||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE);
|
||||||
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE);
|
||||||
|
if (GLAdapter.isGL30()) {
|
||||||
|
gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.DEPTH_ATTACHMENT, GL.TEXTURE_2D, texture[0], 0);
|
||||||
|
} else {
|
||||||
|
gl.framebufferTexture2D(GL.FRAMEBUFFER, GL.COLOR_ATTACHMENT0, GL.TEXTURE_2D, texture[0], 0);
|
||||||
|
}
|
||||||
|
return texture[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateViewportDimensions() {
|
||||||
|
defaultWidth = GLState.getViewportWidth();
|
||||||
|
defaultHeight = GLState.getViewportHeight();
|
||||||
|
}
|
||||||
|
}
|
||||||
328
vtm/src/org/oscim/renderer/light/ShadowRenderer.java
Normal file
328
vtm/src/org/oscim/renderer/light/ShadowRenderer.java
Normal file
@ -0,0 +1,328 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019 Gustl22
|
||||||
|
* Copyright 2019 schedul-xor
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify it under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along with
|
||||||
|
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.oscim.renderer.light;
|
||||||
|
|
||||||
|
import org.oscim.backend.GL;
|
||||||
|
import org.oscim.renderer.ExtrusionRenderer;
|
||||||
|
import org.oscim.renderer.GLMatrix;
|
||||||
|
import org.oscim.renderer.GLShader;
|
||||||
|
import org.oscim.renderer.GLState;
|
||||||
|
import org.oscim.renderer.GLUtils;
|
||||||
|
import org.oscim.renderer.GLViewport;
|
||||||
|
import org.oscim.renderer.LayerRenderer;
|
||||||
|
import org.oscim.renderer.MapRenderer;
|
||||||
|
import org.oscim.utils.math.MathUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.nio.FloatBuffer;
|
||||||
|
|
||||||
|
import static org.oscim.backend.GLAdapter.gl;
|
||||||
|
|
||||||
|
public class ShadowRenderer extends LayerRenderer {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ShadowRenderer.class);
|
||||||
|
|
||||||
|
public static boolean DEBUG = false;
|
||||||
|
|
||||||
|
private ExtrusionRenderer mRenderer;
|
||||||
|
|
||||||
|
private float SHADOWMAP_RESOLUTION = 2048f;
|
||||||
|
private int mGroundQuad;
|
||||||
|
//private int mGroundShadowQuad;
|
||||||
|
private ShadowFrameBuffer mFrameBuffer;
|
||||||
|
|
||||||
|
private float[] mOrthoMat = new float[16];
|
||||||
|
private float[] mViewProjTmp = new float[16];
|
||||||
|
private GLMatrix mLightMat = new GLMatrix();
|
||||||
|
private GLMatrix mRotTmp = new GLMatrix();
|
||||||
|
|
||||||
|
static float[] texUnitConverterF = new float[]{
|
||||||
|
0.5f, 0.0f, 0.0f, 0.0f,
|
||||||
|
0.0f, 0.5f, 0.0f, 0.0f,
|
||||||
|
0.0f, 0.0f, 0.5f, 0.0f,
|
||||||
|
0.5f, 0.5f, 0.5f, 1.0f};
|
||||||
|
static GLMatrix texUnitConverter = new GLMatrix();
|
||||||
|
|
||||||
|
static {
|
||||||
|
texUnitConverter.set(texUnitConverterF);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shader to draw the extrusions.
|
||||||
|
*/
|
||||||
|
private Shader mExtrusionShader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shader to draw the ground.
|
||||||
|
*/
|
||||||
|
private GroundShader mGroundShader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shader to create shadow map (of ground and extrusions) from lights view.
|
||||||
|
*/
|
||||||
|
private ExtrusionRenderer.Shader mLightShader;
|
||||||
|
|
||||||
|
public static class GroundShader extends GLShader {
|
||||||
|
int aPos, uLightColor, uLightMvp, uMVP, uShadowMap, uShadowRes;
|
||||||
|
|
||||||
|
public GroundShader(String shader) {
|
||||||
|
if (!createDirective(shader, "#define SHADOW 1\n"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
aPos = getAttrib("a_pos");
|
||||||
|
uLightColor = getUniform("u_lightColor");
|
||||||
|
uLightMvp = getUniform("u_light_mvp");
|
||||||
|
uMVP = getUniform("u_mvp");
|
||||||
|
uShadowMap = getUniform("u_shadowMap");
|
||||||
|
uShadowRes = getUniform("u_shadowRes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Shader extends ExtrusionRenderer.Shader {
|
||||||
|
/**
|
||||||
|
* For temporary use.
|
||||||
|
*/
|
||||||
|
static final GLMatrix lightMvp = new GLMatrix();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The light view projection matrix.
|
||||||
|
*/
|
||||||
|
GLMatrix lightMat = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The light color and shadow transparency as uniform.
|
||||||
|
*/
|
||||||
|
int uLightColor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The light mvp for shadow as uniform.
|
||||||
|
*/
|
||||||
|
int uLightMvp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The shadow map texture as uniform.
|
||||||
|
*/
|
||||||
|
int uShadowMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The shadow map resolution as uniform.
|
||||||
|
*/
|
||||||
|
int uShadowRes;
|
||||||
|
|
||||||
|
public Shader(String shader) {
|
||||||
|
super(shader, "#define SHADOW 1\n");
|
||||||
|
uLightColor = getUniform("u_lightColor");
|
||||||
|
uLightMvp = getUniform("u_light_mvp");
|
||||||
|
uShadowMap = getUniform("u_shadowMap");
|
||||||
|
uShadowRes = getUniform("u_shadowRes");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLightMVP(GLMatrix model) {
|
||||||
|
if (lightMat == null) return;
|
||||||
|
synchronized (lightMvp) {
|
||||||
|
lightMvp.copy(model);
|
||||||
|
lightMvp.multiplyLhs(lightMat);
|
||||||
|
//lightMvp.addDepthOffset(delta);
|
||||||
|
lightMvp.setAsUniform(uLightMvp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ShadowRenderer(ExtrusionRenderer renderer) {
|
||||||
|
setRenderer(renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRenderer(ExtrusionRenderer renderer) {
|
||||||
|
mRenderer = renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bind a plane to easily draw all over the ground.
|
||||||
|
*/
|
||||||
|
private static int bindPlane(float width, float height) {
|
||||||
|
int vertexBuffer;
|
||||||
|
int[] vboIds = GLUtils.glGenBuffers(1);
|
||||||
|
FloatBuffer floatBuffer = MapRenderer.getFloatBuffer(8);
|
||||||
|
// indices: 0 1 2 - 2 1 3
|
||||||
|
float[] quad = new float[]{
|
||||||
|
-width, height,
|
||||||
|
width, height,
|
||||||
|
-width, -height,
|
||||||
|
width, -height
|
||||||
|
};
|
||||||
|
floatBuffer.put(quad);
|
||||||
|
floatBuffer.flip();
|
||||||
|
vertexBuffer = vboIds[0];
|
||||||
|
GLState.bindVertexBuffer(vertexBuffer);
|
||||||
|
gl.bufferData(GL.ARRAY_BUFFER,
|
||||||
|
quad.length * 4, floatBuffer,
|
||||||
|
GL.STATIC_DRAW);
|
||||||
|
GLState.bindVertexBuffer(GLState.UNBIND);
|
||||||
|
return vertexBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setup() {
|
||||||
|
// Ground plane for shadow map
|
||||||
|
//mGroundShadowQuad = bindPlane(SHADOWMAP_RESOLUTION * 1.1f, SHADOWMAP_RESOLUTION * 1.1f);
|
||||||
|
if (!DEBUG) {
|
||||||
|
// Ground plane to draw shadows an
|
||||||
|
mGroundQuad = bindPlane(Short.MAX_VALUE, Short.MAX_VALUE);
|
||||||
|
} else {
|
||||||
|
mGroundQuad = bindPlane(SHADOWMAP_RESOLUTION * 1.1f, SHADOWMAP_RESOLUTION * 1.1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shader
|
||||||
|
mGroundShader = new GroundShader("extrusion_shadow_ground");
|
||||||
|
mLightShader = new ExtrusionRenderer.Shader("extrusion_shadow_light");
|
||||||
|
if (mRenderer.isMesh())
|
||||||
|
mExtrusionShader = new Shader("extrusion_layer_mesh");
|
||||||
|
else
|
||||||
|
mExtrusionShader = new Shader("extrusion_layer_ext");
|
||||||
|
|
||||||
|
mFrameBuffer = new ShadowFrameBuffer((int) SHADOWMAP_RESOLUTION, (int) SHADOWMAP_RESOLUTION);
|
||||||
|
|
||||||
|
//mRenderer.setup(); // No need to setup, as shaders are taken from here
|
||||||
|
|
||||||
|
return super.setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(GLViewport viewport) {
|
||||||
|
mRenderer.update(viewport);
|
||||||
|
setReady(mRenderer.isReady());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(GLViewport viewport) {
|
||||||
|
/* Prepare rendering from lights view: */
|
||||||
|
// Store vp-matrix temporarily and use vp-matrix as lightMat
|
||||||
|
viewport.viewproj.get(mViewProjTmp);
|
||||||
|
|
||||||
|
float projWidth = SHADOWMAP_RESOLUTION;
|
||||||
|
float projHeight = SHADOWMAP_RESOLUTION;
|
||||||
|
if (DEBUG) {
|
||||||
|
projWidth *= (3. / 4);
|
||||||
|
projHeight *= (3. / 4);
|
||||||
|
}
|
||||||
|
GLMatrix.orthoM(mOrthoMat, 0, -projWidth, projWidth, projHeight, -projHeight, -SHADOWMAP_RESOLUTION, SHADOWMAP_RESOLUTION);
|
||||||
|
viewport.viewproj.set(mOrthoMat);
|
||||||
|
|
||||||
|
// Rotate from light direction
|
||||||
|
float[] lightPos = mRenderer.getSun().getPosition();
|
||||||
|
float rot = (float) Math.acos(lightPos[2] / 1.) * MathUtils.radiansToDegrees;
|
||||||
|
mRotTmp.setRotation(rot, 1f, 0, 0); // tilt
|
||||||
|
viewport.viewproj.multiplyRhs(mRotTmp);
|
||||||
|
|
||||||
|
rot = MathUtils.atan2(lightPos[0], lightPos[1]) * MathUtils.radiansToDegrees;
|
||||||
|
mRotTmp.setRotation(rot, 0, 0, 1f); // bearing
|
||||||
|
viewport.viewproj.multiplyRhs(mRotTmp);
|
||||||
|
|
||||||
|
// DRAW SHADOW MAP
|
||||||
|
{
|
||||||
|
// START DEPTH FRAMEBUFFER
|
||||||
|
mFrameBuffer.bindFrameBuffer();
|
||||||
|
|
||||||
|
GLState.blend(false); // depth cannot be transparent
|
||||||
|
gl.depthMask(true);
|
||||||
|
GLState.test(true, false);
|
||||||
|
|
||||||
|
// Clear color (for gl20) and depth (for gl30)
|
||||||
|
gl.clear(gl.COLOR_BUFFER_BIT | GL.DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
// Draw GROUND shadow map (usually the ground does not cast any shadow)
|
||||||
|
/*{
|
||||||
|
mLightShader.useProgram();
|
||||||
|
|
||||||
|
viewport.viewproj.setAsUniform(mLightShader.uMVP);
|
||||||
|
gl.uniform1f(mLightShader.uAlpha, 1f);
|
||||||
|
GLState.bindVertexBuffer(mGroundShadowQuad);
|
||||||
|
GLState.enableVertexArrays(mLightShader.aPos, GLState.DISABLED);
|
||||||
|
gl.vertexAttribPointer(mLightShader.aPos, 2, GL.FLOAT, false, 0, 0);
|
||||||
|
MapRenderer.bindQuadIndicesVBO();
|
||||||
|
|
||||||
|
gl.drawElements(GL.TRIANGLES, 6, GL.UNSIGNED_SHORT, 0);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// Draw EXTRUSION shadow map (in ExtrusionRenderer)
|
||||||
|
{
|
||||||
|
//ExtrusionRenderer.Shader tmpShader = mRenderer.getShader();
|
||||||
|
mRenderer.setShader(mLightShader);
|
||||||
|
mRenderer.useLight(false);
|
||||||
|
mRenderer.render(viewport);
|
||||||
|
//mRenderer.setShader(tmpShader);
|
||||||
|
}
|
||||||
|
|
||||||
|
// END DEPTH FRAMEBUFFER
|
||||||
|
mFrameBuffer.unbindFrameBuffer();
|
||||||
|
}
|
||||||
|
mLightMat.copy(viewport.viewproj); // save lightMat
|
||||||
|
mLightMat.multiplyLhs(texUnitConverter); // apply shadow map converter to mLightMat
|
||||||
|
|
||||||
|
viewport.viewproj.set(mViewProjTmp); // write back stored vp-matrix
|
||||||
|
|
||||||
|
// DRAW SCENE
|
||||||
|
{
|
||||||
|
int lightColor = mRenderer.getSun().getColor();
|
||||||
|
GLState.test(false, false);
|
||||||
|
gl.clear(GL.DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
// Bind shadow map texture
|
||||||
|
gl.activeTexture(gl.TEXTURE2);
|
||||||
|
GLState.bindTex2D(mFrameBuffer.getShadowMap());
|
||||||
|
|
||||||
|
// Draw GROUND
|
||||||
|
{
|
||||||
|
mGroundShader.useProgram();
|
||||||
|
viewport.viewproj.setAsUniform(mGroundShader.uMVP);
|
||||||
|
|
||||||
|
gl.uniform1i(mGroundShader.uShadowMap, 2); // TEXTURE2 for shadows
|
||||||
|
GLUtils.setColor(mGroundShader.uLightColor, lightColor);
|
||||||
|
gl.uniform1f(mGroundShader.uShadowRes, SHADOWMAP_RESOLUTION);
|
||||||
|
mLightMat.setAsUniform(mGroundShader.uLightMvp);
|
||||||
|
|
||||||
|
// Bind VBO
|
||||||
|
GLState.bindVertexBuffer(mGroundQuad);
|
||||||
|
GLState.enableVertexArrays(mGroundShader.aPos, GLState.DISABLED);
|
||||||
|
gl.vertexAttribPointer(mGroundShader.aPos, 2, GL.FLOAT, false, 0, 0);
|
||||||
|
MapRenderer.bindQuadIndicesVBO();
|
||||||
|
GLState.blend(true); // allow transparency
|
||||||
|
gl.blendFunc(GL.ZERO, GL.SRC_COLOR); // multiply frame colors
|
||||||
|
gl.drawElements(GL.TRIANGLES, 6, GL.UNSIGNED_SHORT, 0);
|
||||||
|
GLState.blend(false);
|
||||||
|
gl.blendFunc(GL.ONE, GL.ONE_MINUS_SRC_ALPHA); // Reset to default func
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw EXTRUSIONS (in ExtrusionRenderer)
|
||||||
|
{
|
||||||
|
//ExtrusionRenderer.Shader tmpShader = mRenderer.getShader();
|
||||||
|
mExtrusionShader.useProgram();
|
||||||
|
gl.uniform1i(mExtrusionShader.uShadowMap, 2); // TEXTURE2 for shadows
|
||||||
|
GLUtils.setColor(mExtrusionShader.uLightColor, lightColor);
|
||||||
|
gl.uniform1f(mExtrusionShader.uShadowRes, SHADOWMAP_RESOLUTION);
|
||||||
|
|
||||||
|
mExtrusionShader.lightMat = mLightMat;
|
||||||
|
mRenderer.setShader(mExtrusionShader);
|
||||||
|
mRenderer.useLight(true);
|
||||||
|
mRenderer.render(viewport);
|
||||||
|
//mRenderer.setShader(tmpShader);
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.activeTexture(GL.TEXTURE0); // reset active Texture
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user