Browse Source

Added RSM sample. (#854)

* Added RSM sample.
jcherlin 9 years ago
parent
commit
f56cf4c88e

+ 131 - 0
examples/31-reflectiveshadowmap/fs_rsm_combine.sc

@@ -0,0 +1,131 @@
+$input v_texcoord0
+
+/*
+ * Copyright 2016 Joseph Cherlin. All rights reserved.
+ * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
+ */
+
+#include "../common/common.sh"
+
+SAMPLER2D(s_normal, 0);
+SAMPLER2D(s_color,  1);
+SAMPLER2D(s_light,  2);
+SAMPLER2D(s_depth,  3);
+SAMPLER2DSHADOW(s_shadowMap, 4);
+
+// Single directional light for entire scene
+uniform vec4 u_lightDir;
+uniform mat4 u_invMvp;
+uniform mat4 u_lightMtx;
+uniform vec4 u_shadowDimsInv;
+uniform vec4 u_rsmAmount;
+
+float hardShadow(sampler2DShadow _sampler, vec4 _shadowCoord, float _bias)
+{
+    vec2 texCoord = _shadowCoord.xy;
+	return shadow2D(_sampler, vec3(texCoord.xy, _shadowCoord.z-_bias) );
+}
+
+float PCF(sampler2DShadow _sampler, vec4 _shadowCoord, float _bias, vec2 _texelSize)
+{
+	vec2 texCoord = _shadowCoord.xy;
+
+	bool outside = any(greaterThan(texCoord, vec2_splat(1.0)))
+				|| any(lessThan   (texCoord, vec2_splat(0.0)))
+				 ;
+
+	if (outside)
+	{
+		return 1.0;
+	}
+
+	float result = 0.0;
+	vec2 offset = _texelSize * _shadowCoord.w;
+
+	result += hardShadow(_sampler, _shadowCoord + vec4(vec2(-1.5, -1.5) * offset, 0.0, 0.0), _bias);
+	result += hardShadow(_sampler, _shadowCoord + vec4(vec2(-1.5, -0.5) * offset, 0.0, 0.0), _bias);
+	result += hardShadow(_sampler, _shadowCoord + vec4(vec2(-1.5,  0.5) * offset, 0.0, 0.0), _bias);
+	result += hardShadow(_sampler, _shadowCoord + vec4(vec2(-1.5,  1.5) * offset, 0.0, 0.0), _bias);
+
+	result += hardShadow(_sampler, _shadowCoord + vec4(vec2(-0.5, -1.5) * offset, 0.0, 0.0), _bias);
+	result += hardShadow(_sampler, _shadowCoord + vec4(vec2(-0.5, -0.5) * offset, 0.0, 0.0), _bias);
+	result += hardShadow(_sampler, _shadowCoord + vec4(vec2(-0.5,  0.5) * offset, 0.0, 0.0), _bias);
+	result += hardShadow(_sampler, _shadowCoord + vec4(vec2(-0.5,  1.5) * offset, 0.0, 0.0), _bias);
+
+	result += hardShadow(_sampler, _shadowCoord + vec4(vec2(0.5, -1.5) * offset, 0.0, 0.0), _bias);
+	result += hardShadow(_sampler, _shadowCoord + vec4(vec2(0.5, -0.5) * offset, 0.0, 0.0), _bias);
+	result += hardShadow(_sampler, _shadowCoord + vec4(vec2(0.5,  0.5) * offset, 0.0, 0.0), _bias);
+	result += hardShadow(_sampler, _shadowCoord + vec4(vec2(0.5,  1.5) * offset, 0.0, 0.0), _bias);
+
+	result += hardShadow(_sampler, _shadowCoord + vec4(vec2(1.5, -1.5) * offset, 0.0, 0.0), _bias);
+	result += hardShadow(_sampler, _shadowCoord + vec4(vec2(1.5, -0.5) * offset, 0.0, 0.0), _bias);
+	result += hardShadow(_sampler, _shadowCoord + vec4(vec2(1.5,  0.5) * offset, 0.0, 0.0), _bias);
+	result += hardShadow(_sampler, _shadowCoord + vec4(vec2(1.5,  1.5) * offset, 0.0, 0.0), _bias);
+
+	return result / 16.0;
+}
+
+
+float toClipSpaceDepth(float _depthTextureZ)
+{
+#if BGFX_SHADER_LANGUAGE_HLSL || BGFX_SHADER_LANGUAGE_METAL
+    return _depthTextureZ;
+#else
+    return _depthTextureZ * 2.0 - 1.0;
+#endif // BGFX_SHADER_LANGUAGE_HLSL || BGFX_SHADER_LANGUAGE_METAL
+}
+
+vec3 clipToWorld(mat4 _invViewProj, vec3 _clipPos)
+{
+    vec4 wpos = mul(_invViewProj, vec4(_clipPos, 1.0) );
+    return wpos.xyz / wpos.w;
+}
+
+
+void main()
+{
+	vec3 n  = texture2D(s_normal, v_texcoord0).xyz;
+    // Expand out normal
+    n = n*2.0+-1.0;
+    vec3 l = u_lightDir.xyz;//normalize(vec3(-0.8,0.75,-1.0));
+    float dirLightIntensity = 1.0;
+    float dirLight = max(0.0,dot(n,l)) * dirLightIntensity;
+
+    // Apply shadow map
+    
+    // Get world position so we can transform it into light space, to look into shadow map
+    vec2 texCoord = v_texcoord0.xy;
+    float deviceDepth = texture2D(s_depth, texCoord).x;
+    float depth       = toClipSpaceDepth(deviceDepth);
+    vec3 clip = vec3(texCoord * 2.0 - 1.0, depth);
+#if BGFX_SHADER_LANGUAGE_HLSL || BGFX_SHADER_LANGUAGE_METAL
+    clip.y = -clip.y;
+#endif // BGFX_SHADER_LANGUAGE_HLSL || BGFX_SHADER_LANGUAGE_METAL
+    vec3 wpos = clipToWorld(u_invMvp, clip);
+
+    const float shadowMapOffset = 0.003;
+    vec3 posOffset = wpos + n.xyz * shadowMapOffset;
+    vec4 shadowCoord = mul(u_lightMtx, vec4(posOffset, 1.0) );
+
+#if BGFX_SHADER_LANGUAGE_HLSL || BGFX_SHADER_LANGUAGE_METAL
+    shadowCoord.y *= -1.0;
+#endif // BGFX_SHADER_LANGUAGE_HLSL || BGFX_SHADER_LANGUAGE_METAL
+    
+    float shadowMapBias = 0.001;
+    vec2 texelSize = vec2_splat(u_shadowDimsInv.x);
+
+    shadowCoord.xy /= shadowCoord.w;
+    shadowCoord.xy = shadowCoord.xy*0.5+0.5;
+
+    float visibility = PCF(s_shadowMap, shadowCoord, shadowMapBias, texelSize);
+
+    dirLight *= visibility;
+    
+    // Light from light buffer
+    vec3 albedo = texture2D(s_color, v_texcoord0).xyz;
+    vec3 lightBuffer = texture2D(s_light, v_texcoord0).xyz;
+
+    gl_FragColor.xyz = mix(dirLight * albedo, lightBuffer * albedo, u_rsmAmount.x);
+
+    gl_FragColor.w = 1.0;
+}

+ 22 - 0
examples/31-reflectiveshadowmap/fs_rsm_gbuffer.sc

@@ -0,0 +1,22 @@
+$input v_normal
+
+/*
+ * Copyright 2016 Joseph Cherlin. All rights reserved.
+ * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
+ */
+
+#include "../common/common.sh"
+
+uniform vec4 u_tint;
+
+void main()
+{
+    vec3 normalWorldSpace = v_normal;
+
+    // Write normal
+    gl_FragData[0].xyz = normalWorldSpace.xyz; // Normal is already compressed to [0,1] so can fit in gbuffer
+    gl_FragData[0].w = 0.0;
+
+    // Write color
+    gl_FragData[1] = u_tint;
+}

+ 67 - 0
examples/31-reflectiveshadowmap/fs_rsm_lbuffer.sc

@@ -0,0 +1,67 @@
+$input v_lightCenterScale, v_color0
+
+/*
+ * Copyright 2016 Joseph Cherlin. All rights reserved.
+ * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
+ */
+
+#include "../common/common.sh"
+
+SAMPLER2D(s_normal, 0); // Normal output from gbuffer
+SAMPLER2D(s_depth,  1);  // Depth output from gbuffer
+
+uniform mat4 u_invMvp;
+
+float toClipSpaceDepth(float _depthTextureZ)
+{
+#if BGFX_SHADER_LANGUAGE_HLSL || BGFX_SHADER_LANGUAGE_METAL
+    return _depthTextureZ;
+#else
+    return _depthTextureZ * 2.0 - 1.0;
+#endif // BGFX_SHADER_LANGUAGE_HLSL || BGFX_SHADER_LANGUAGE_METAL
+}
+
+vec3 clipToWorld(mat4 _invViewProj, vec3 _clipPos)
+{
+    vec4 wpos = mul(_invViewProj, vec4(_clipPos, 1.0) );
+    return wpos.xyz / wpos.w;
+}
+
+void main()
+{
+#if BGFX_SHADER_LANGUAGE_HLSL && (BGFX_SHADER_LANGUAGE_HLSL < 4)
+    vec2 texCoord = gl_FragCoord.xy * u_viewTexel.xy + u_viewTexel.xy * vec2_splat(0.5);
+#else
+    vec2 texCoord = gl_FragCoord.xy * u_viewTexel.xy;
+#endif
+
+    // Get world position
+    float deviceDepth = texture2D(s_depth, texCoord).x;
+    float depth       = toClipSpaceDepth(deviceDepth);
+
+    vec3 clip = vec3(texCoord * 2.0 - 1.0, depth);
+#if BGFX_SHADER_LANGUAGE_HLSL || BGFX_SHADER_LANGUAGE_METAL
+    clip.y = -clip.y;
+#endif // BGFX_SHADER_LANGUAGE_HLSL || BGFX_SHADER_LANGUAGE_METAL
+    vec3 wpos = clipToWorld(u_invMvp, clip);
+ 
+    // Get normal from its map, and decompress
+    vec3 n  = texture2D(s_normal, texCoord).xyz*2.0-1.0;
+    
+    // Do lighting
+    vec3 pointToLight = v_lightCenterScale.xyz-wpos;
+    float lightLen = sqrt(dot(pointToLight, pointToLight));
+
+    float lightFalloff;
+
+    if (lightLen > v_lightCenterScale.w)
+        lightFalloff = 0.0;
+    else
+        lightFalloff = 1.0-(lightLen/v_lightCenterScale.w);  // Linear falloff for light (could use dist sq if you want)
+
+    vec3 l = normalize(pointToLight)*lightFalloff;
+    
+    gl_FragColor.xyz = v_color0.xyz * max(0.0, dot(n,l));
+
+    gl_FragColor.w = 1.0;
+}

+ 22 - 0
examples/31-reflectiveshadowmap/fs_rsm_shadow.sc

@@ -0,0 +1,22 @@
+$input v_normal
+
+/*
+ * Copyright 2016 Joseph Cherlin. All rights reserved.
+ * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
+ */
+
+#include "../common/common.sh"
+
+uniform vec4 u_tint;
+
+void main()
+{
+#if BGFX_SHADER_LANGUAGE_HLSL && (BGFX_SHADER_LANGUAGE_HLSL < 4)
+    vec2 texCoord = gl_FragCoord.xy * u_viewTexel.xy + u_viewTexel.xy * vec2_splat(0.5);
+#else
+    vec2 texCoord = gl_FragCoord.xy * u_viewTexel.xy;
+#endif
+
+    gl_FragData[0].xyz = u_tint.xyz;  // Color of light sphere
+    gl_FragData[0].w = -v_normal.z;   // Radius of light sphere
+}

+ 18 - 0
examples/31-reflectiveshadowmap/makefile

@@ -0,0 +1,18 @@
+#
+# Copyright 2011-2016 Branimir Karadzic. All rights reserved.
+# License: http://www.opensource.org/licenses/BSD-2-Clause
+#
+
+BGFX_DIR=../..
+RUNTIME_DIR=$(BGFX_DIR)/examples/runtime
+BUILD_DIR=../../.build
+
+include $(BGFX_DIR)/scripts/shader.mk
+
+rebuild:
+	@make -s --no-print-directory TARGET=0 clean all
+	@make -s --no-print-directory TARGET=1 clean all
+	@make -s --no-print-directory TARGET=2 clean all
+	@make -s --no-print-directory TARGET=3 clean all
+	@make -s --no-print-directory TARGET=4 clean all
+	@make -s --no-print-directory TARGET=5 clean all

+ 689 - 0
examples/31-reflectiveshadowmap/reflectiveshadowmap.cpp

@@ -0,0 +1,689 @@
+/*
+ * Copyright 2016 Joseph Cherlin. All rights reserved.
+ * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
+ */
+
+#include "common.h"
+#include "camera.h"
+#include "bgfx_utils.h"
+#include "imgui/imgui.h"
+#include <bx/rng.h>
+
+// Intro:
+// RSM (reflective shadow map) is a technique for global illumination.
+// It is similar to shadow map.  It piggybacks on the shadow map, in fact.
+
+// RSM is compatible with any type of lighting which can handle handle
+// a lot of point lights.  This sample happens to use a deferred renderer,
+// but other types would work.
+
+// Overview:
+// 1.  Draw into G-Buffer
+// 2.  Draw Shadow Map (with RSM piggybacked on)
+// 3.  Populate light buffer
+// 4.  Deferred "combine" pass.
+
+// Details:
+// 1.  G-Buffer:  
+// Typical G-Buffer with normals, color, depth.
+// 2.  RSM:
+// A typical shadow map, except it also outputs to a "RSM" buffer.
+// The RSM contains the color of the item drawn, as well as a scalar value which represents
+// how much light would bounce off of the surface if it were hit with light from the origin
+// of the shadow map.
+// 3.  Light Buffer
+// We draw a lot of spheres into the light buffer.  These spheres are called VPL (virtual
+// point lights).  VPLs represent bounced light, and let us eliminate the classic "ambient"
+// term.  Instead of us supplying their world space position in a transform matrix,
+// VPLs gain their position from the shadow map from step 2, using an unprojection.  They gain
+// their color from the RSM.  You could also store their position in a buffer while drawing shadows,
+// I'm just using depth to keep the sample smaller.
+// 4.  Deferred combine:
+// Typical combine used in almost any sort of deferred renderer.
+
+// References
+// http://www.bpeers.com/blog/?itemid=517
+
+// Render passes
+#define RENDER_PASS_GBUFFER      0  // GBuffer for normals and albedo
+#define RENDER_PASS_SHADOW_MAP   1  // Draw into the shadow map (RSM and regular shadow map at same time)
+#define RENDER_PASS_LIGHT_BUFFER 2  // Light buffer for point lights
+#define RENDER_PASS_COMBINE      3  // Directional light and final result
+
+// Gbuffer has multiple render targets
+#define GBUFFER_RT_NORMAL 0
+#define GBUFFER_RT_COLOR  1
+#define GBUFFER_RT_DEPTH  2
+
+// Shadow map has multiple render targets
+#define SHADOW_RT_RSM   0        // In this algorithm, shadows write lighting info as well.
+#define SHADOW_RT_DEPTH 1        // Shadow maps always write a depth
+
+// Random meshes we draw
+#define MESH_COUNT  6    // Mesh (which is a vert/index buffer)
+#define MODEL_COUNT 222  // In this demo, a model is a mesh plus a transform and a color
+
+#define SHADOW_MAP_DIM 512
+#define LIGHT_DIST 10.0f
+
+// Vertex decl for our screen space quad (used in deferred rendering)
+struct PosTexCoord0Vertex
+{
+    float m_x;
+    float m_y;
+    float m_z;
+    float m_u;
+    float m_v;
+
+    static void init()
+    {
+        ms_decl
+            .begin()
+            .add(bgfx::Attrib::Position,  3, bgfx::AttribType::Float)
+            .add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float)
+            .end();
+    }
+
+    static bgfx::VertexDecl ms_decl;
+};
+bgfx::VertexDecl PosTexCoord0Vertex::ms_decl;
+
+// Utility function to draw a screen space quad for deferred rendering
+void screenSpaceQuad(float _textureWidth, float _textureHeight, float _texelHalf, bool _originBottomLeft, float _width = 1.0f, float _height = 1.0f)
+{
+    if (bgfx::checkAvailTransientVertexBuffer(3, PosTexCoord0Vertex::ms_decl) )
+    {
+        bgfx::TransientVertexBuffer vb;
+        bgfx::allocTransientVertexBuffer(&vb, 3, PosTexCoord0Vertex::ms_decl);
+        PosTexCoord0Vertex* vertex = (PosTexCoord0Vertex*)vb.data;
+
+        const float minx = -_width;
+        const float maxx =  _width;
+        const float miny = 0.0f;
+        const float maxy = _height*2.0f;
+
+        const float texelHalfW = _texelHalf/_textureWidth;
+        const float texelHalfH = _texelHalf/_textureHeight;
+        const float minu = -1.0f + texelHalfW;
+        const float maxu =  1.0f + texelHalfH;
+
+        const float zz = 0.0f;
+
+        float minv = texelHalfH;
+        float maxv = 2.0f + texelHalfH;
+
+        if (_originBottomLeft)
+        {
+            float temp = minv;
+            minv = maxv;
+            maxv = temp;
+
+            minv -= 1.0f;
+            maxv -= 1.0f;
+        }
+
+        vertex[0].m_x = minx;
+        vertex[0].m_y = miny;
+        vertex[0].m_z = zz;
+        vertex[0].m_u = minu;
+        vertex[0].m_v = minv;
+
+        vertex[1].m_x = maxx;
+        vertex[1].m_y = miny;
+        vertex[1].m_z = zz;
+        vertex[1].m_u = maxu;
+        vertex[1].m_v = minv;
+
+        vertex[2].m_x = maxx;
+        vertex[2].m_y = maxy;
+        vertex[2].m_z = zz;
+        vertex[2].m_u = maxu;
+        vertex[2].m_v = maxv;
+
+        bgfx::setVertexBuffer(&vb);
+    }
+}
+
+class ExampleReflectiveShadowMap : public entry::AppI
+{
+	void init(int _argc, char** _argv) BX_OVERRIDE
+	{
+		Args args(_argc, _argv);
+
+		m_width  = 1280;
+		m_height = 720;
+		m_debug  = BGFX_DEBUG_TEXT;
+		m_reset  = BGFX_RESET_VSYNC;
+
+		bgfx::init(args.m_type, args.m_pciId);
+
+		bgfx::reset(m_width, m_height, m_reset);
+
+		// Enable debug text.
+		bgfx::setDebug(m_debug);
+
+        // Labeling for renderdoc captures, etc
+        bgfx::setViewName(RENDER_PASS_GBUFFER,      "gbuffer"     );     
+        bgfx::setViewName(RENDER_PASS_SHADOW_MAP,   "shadow map"  );
+        bgfx::setViewName(RENDER_PASS_LIGHT_BUFFER, "light buffer");
+        bgfx::setViewName(RENDER_PASS_COMBINE,      "post combine");
+
+		// Set up screen clears
+		bgfx::setViewClear(RENDER_PASS_GBUFFER
+			, BGFX_CLEAR_COLOR|BGFX_CLEAR_DEPTH
+			, 0
+			, 1.0f
+			, 0
+			);
+
+        bgfx::setViewClear(RENDER_PASS_LIGHT_BUFFER
+            , BGFX_CLEAR_COLOR|BGFX_CLEAR_DEPTH
+            , 0
+            , 1.0f
+            , 0
+            );
+
+        bgfx::setViewClear(RENDER_PASS_SHADOW_MAP
+            , BGFX_CLEAR_COLOR|BGFX_CLEAR_DEPTH
+            , 0
+            , 1.0f
+            , 0
+            );
+
+        // Create uniforms
+        u_tint          = bgfx::createUniform("u_tint",          bgfx::UniformType::Vec4);  // Tint for when you click on items
+        u_lightDir      = bgfx::createUniform("u_lightDir",      bgfx::UniformType::Vec4);  // Single directional light for entire scene
+        u_sphereInfo    = bgfx::createUniform("u_sphereInfo",    bgfx::UniformType::Vec4);  // Info for RSM
+        u_invMvp        = bgfx::createUniform("u_invMvp",        bgfx::UniformType::Mat4);  // Matrix needed in light buffer
+        u_invMvpShadow  = bgfx::createUniform("u_invMvpShadow",  bgfx::UniformType::Mat4);  // Matrix needed in light buffer
+        u_lightMtx      = bgfx::createUniform("u_lightMtx",      bgfx::UniformType::Mat4);  // Matrix needed to use shadow map (world to shadow space)
+        u_shadowDimsInv = bgfx::createUniform("u_shadowDimsInv", bgfx::UniformType::Vec4);  // Used in PCF
+        u_rsmAmount     = bgfx::createUniform("u_rsmAmount",     bgfx::UniformType::Vec4);  // How much RSM to use vs directional light
+
+        // Create texture sampler uniforms (used when we bind textures)
+        s_normal    = bgfx::createUniform("s_normal",    bgfx::UniformType::Int1);  // Normal gbuffer
+        s_depth     = bgfx::createUniform("s_depth",     bgfx::UniformType::Int1);  // Normal gbuffer
+        s_color     = bgfx::createUniform("s_color",     bgfx::UniformType::Int1);  // Color (albedo) gbuffer
+        s_light     = bgfx::createUniform("s_light",     bgfx::UniformType::Int1);  // Light buffer
+        s_shadowMap = bgfx::createUniform("s_shadowMap", bgfx::UniformType::Int1);  // Shadow map
+        s_rsm       = bgfx::createUniform("s_rsm",       bgfx::UniformType::Int1);  // Reflective shadow map
+        
+		// Create program from shaders.
+		m_gbufferProgram = loadProgram("vs_rsm_gbuffer", "fs_rsm_gbuffer");  // Gbuffer
+        m_shadowProgram  = loadProgram("vs_rsm_shadow",  "fs_rsm_shadow"  ); // Drawing shadow map
+        m_lightProgram   = loadProgram("vs_rsm_lbuffer", "fs_rsm_lbuffer");  // Light buffer
+        m_combineProgram = loadProgram("vs_rsm_combine", "fs_rsm_combine");  // Combiner
+
+        // Load some meshes
+        for (uint32_t i = 0; i < MESH_COUNT; i++) {
+            m_meshes[i] = meshLoad(m_meshPaths[i]);
+        }
+
+        // Randomly create some models
+        bx::RngMwc mwc;  // Random number generator
+        for (Model & m : m_models) {
+            uint32_t r = mwc.gen() % 256;
+            uint32_t g = mwc.gen() % 256;
+            uint32_t b = mwc.gen() % 256;
+            m.mesh = 1+mwc.gen()%(MESH_COUNT-1);
+            m.color[0] = r/255.0f;
+            m.color[1] = g/255.0f;
+            m.color[2] = b/255.0f;
+            m.color[3] = 1.0f;
+            m.position[0] = (((mwc.gen() % 256)) - 128.0f)/20.0f;
+            m.position[1] = 0;
+            m.position[2] = (((mwc.gen() % 256)) - 128.0f)/20.0f;
+        }
+
+        // Load ground.  We'll just use the cube since I don't have a ground model right now
+        m_ground = meshLoad("meshes/cube.bin");
+
+        // Light sphere
+        m_lightSphere = meshLoad("meshes/unit_sphere.bin");
+
+        const uint32_t samplerFlags = 0
+            | BGFX_TEXTURE_RT
+            | BGFX_TEXTURE_MIN_POINT
+            | BGFX_TEXTURE_MAG_POINT
+            | BGFX_TEXTURE_MIP_POINT
+            | BGFX_TEXTURE_U_CLAMP
+            | BGFX_TEXTURE_V_CLAMP
+            ;
+
+        // Make gbuffer and related textures
+        m_gbufferTex[GBUFFER_RT_NORMAL] = bgfx::createTexture2D(bgfx::BackbufferRatio::Equal, 1, bgfx::TextureFormat::BGRA8, samplerFlags);
+        m_gbufferTex[GBUFFER_RT_COLOR]  = bgfx::createTexture2D(bgfx::BackbufferRatio::Equal, 1, bgfx::TextureFormat::BGRA8, samplerFlags);
+        m_gbufferTex[GBUFFER_RT_DEPTH]  = bgfx::createTexture2D(bgfx::BackbufferRatio::Equal, 1, bgfx::TextureFormat::D24,   samplerFlags);
+        m_gbuffer = bgfx::createFrameBuffer(BX_COUNTOF(m_gbufferTex), m_gbufferTex, true);
+
+        // Make light buffer
+        m_lightBufferTex = bgfx::createTexture2D(bgfx::BackbufferRatio::Equal, 1, bgfx::TextureFormat::BGRA8, samplerFlags);
+        bgfx::TextureHandle lightBufferRTs[] =  {
+            m_lightBufferTex
+        };
+        m_lightBuffer = bgfx::createFrameBuffer(BX_COUNTOF(lightBufferRTs), lightBufferRTs, true);
+
+        // Make shadow buffer
+        const uint32_t rsmFlags = 0
+            | BGFX_TEXTURE_RT
+            | BGFX_TEXTURE_MIN_POINT
+            | BGFX_TEXTURE_MAG_POINT
+            | BGFX_TEXTURE_MIP_POINT
+            | BGFX_TEXTURE_U_CLAMP
+            | BGFX_TEXTURE_V_CLAMP
+            ;
+
+        // Reflective shadow map
+        m_shadowBufferTex[SHADOW_RT_RSM] = bgfx::createTexture2D(
+            SHADOW_MAP_DIM
+            , SHADOW_MAP_DIM
+            , 1
+            , bgfx::TextureFormat::BGRA8,
+            rsmFlags
+            );
+       
+        // Typical shadow map
+        m_shadowBufferTex[SHADOW_RT_DEPTH] = bgfx::createTexture2D(
+            SHADOW_MAP_DIM
+            , SHADOW_MAP_DIM
+            , 1
+            , bgfx::TextureFormat::D16,
+            BGFX_TEXTURE_RT/* | BGFX_TEXTURE_COMPARE_LEQUAL*/
+            );  // Note I'm not setting BGFX_TEXTURE_COMPARE_LEQUAL.  Why?
+        // Normally a PCF shadow map such as this requires a compare.  However, this sample also
+        // reads from this texture in the lighting pass, and only uses the PCF capabilites in the
+        // combine pass, so the flag is disabled by default.
+        
+        m_shadowBuffer = bgfx::createFrameBuffer(BX_COUNTOF(m_shadowBufferTex), m_shadowBufferTex, true);
+
+        // Vertex decl
+        PosTexCoord0Vertex::init();
+
+        // Init camera
+        cameraCreate();
+        float camPos[] = {0.0f, 1.5f, 0.0f};
+        cameraSetPosition(camPos);
+        cameraSetVerticalAngle(-0.3f);
+
+        // Init directional light
+        updateLightDir();
+    
+        // Get renderer capabilities info.
+        m_caps = bgfx::getCaps();
+        const bgfx::RendererType::Enum renderer = bgfx::getRendererType();
+        m_texelHalf = bgfx::RendererType::Direct3D9 == renderer ? 0.5f : 0.0f;
+
+        imguiCreate();
+    }
+
+	int shutdown() BX_OVERRIDE
+	{
+        for (uint32_t i = 0; i < MESH_COUNT; i++) {
+            meshUnload(m_meshes[i]);
+        }
+        meshUnload(m_ground);
+        meshUnload(m_lightSphere);
+
+		// Cleanup.
+		bgfx::destroyProgram(m_gbufferProgram);
+        bgfx::destroyProgram(m_lightProgram);
+        bgfx::destroyProgram(m_combineProgram);
+        bgfx::destroyProgram(m_shadowProgram);
+
+		bgfx::destroyUniform(u_tint);
+        bgfx::destroyUniform(u_lightDir);
+        bgfx::destroyUniform(u_sphereInfo);
+        bgfx::destroyUniform(u_invMvp);
+        bgfx::destroyUniform(u_invMvpShadow);
+        bgfx::destroyUniform(u_lightMtx);
+        bgfx::destroyUniform(u_shadowDimsInv);
+        bgfx::destroyUniform(u_rsmAmount);
+        bgfx::destroyUniform(s_normal);
+        bgfx::destroyUniform(s_depth);
+        bgfx::destroyUniform(s_light);
+        bgfx::destroyUniform(s_color);
+        bgfx::destroyUniform(s_shadowMap);
+        bgfx::destroyUniform(s_rsm);
+
+        bgfx::destroyFrameBuffer(m_gbuffer);
+        bgfx::destroyFrameBuffer(m_lightBuffer);
+        bgfx::destroyFrameBuffer(m_shadowBuffer);
+
+        for (uint32_t i = 0; i < BX_COUNTOF(m_gbufferTex); i++)
+            bgfx::destroyTexture(m_gbufferTex[i]);
+
+        bgfx::destroyTexture(m_lightBufferTex);
+        for (uint32_t i = 0; i < BX_COUNTOF(m_shadowBufferTex); i++)
+            bgfx::destroyTexture(m_shadowBufferTex[i]);
+
+        cameraDestroy();
+
+        imguiDestroy();
+
+		// Shutdown bgfx.
+		bgfx::shutdown();
+
+		return 0;
+	}
+
+	bool update() BX_OVERRIDE
+	{
+		if (!entry::processEvents(m_width, m_height, m_debug, m_reset, &m_mouseState) )
+		{
+            // Update frame timer
+            int64_t now = bx::getHPCounter();
+            static int64_t last = now;
+            const int64_t frameTime = now - last;
+            last = now;
+            const double freq = double(bx::getHPFrequency());
+            const double toMs = 1000.0 / freq;
+            const float deltaTime = float(frameTime/freq);
+
+			// Use debug font to print information about this example.
+			bgfx::dbgTextClear();
+			bgfx::dbgTextPrintf(0, 1, 0x4f, "bgfx/examples/31-reflectiveshadowmap");
+			bgfx::dbgTextPrintf(0, 2, 0x6f, "Description: GI via reflective shadow map.");
+			bgfx::dbgTextPrintf(0, 3, 0x0f, "Frame: % 7.3f[ms]", double(frameTime)*toMs);
+
+            // Update camera
+            cameraUpdate(deltaTime*0.15f, m_mouseState);
+
+            // Set up matrices for gbuffer
+			float view[16];
+			cameraGetViewMtx(view);
+
+			float proj[16];
+			bx::mtxProj(proj, 60.0f, float(m_width)/float(m_height), 0.1f, 100.0f);
+
+            bgfx::setViewRect(RENDER_PASS_GBUFFER, 0, 0, uint16_t(m_width), uint16_t(m_height));
+            bgfx::setViewTransform(RENDER_PASS_GBUFFER, view, proj);
+            // Make sure when we draw it goes into gbuffer and not backbuffer
+            bgfx::setViewFrameBuffer(RENDER_PASS_GBUFFER, m_gbuffer);
+            // Draw everything into g-buffer
+            drawAllModels(RENDER_PASS_GBUFFER, m_gbufferProgram);
+            
+            // Draw shadow map
+            
+            // Set up transforms for shadow map
+            float smView[16], smProj[16], lightEye[3], lightAt[3];
+            lightEye[0] = m_lightDir[0]*LIGHT_DIST;
+            lightEye[1] = m_lightDir[1]*LIGHT_DIST;
+            lightEye[2] = m_lightDir[2]*LIGHT_DIST;
+
+            lightAt[0] = 0.0f;
+            lightAt[1] = 0.0f;
+            lightAt[2] = 0.0f;
+
+            bx::mtxLookAt(smView, lightEye, lightAt);
+            const float area = 10.0f;
+            bgfx::RendererType::Enum renderer = bgfx::getRendererType();
+            bool flipV = false
+                || renderer == bgfx::RendererType::OpenGL
+                || renderer == bgfx::RendererType::OpenGLES
+                ;
+            bx::mtxOrtho(smProj, -area, area, -area, area, -100.0f, 100.0f, 0.0f, flipV);
+            bgfx::setViewTransform(RENDER_PASS_SHADOW_MAP, smView, smProj);
+            bgfx::setViewFrameBuffer(RENDER_PASS_SHADOW_MAP, m_shadowBuffer);
+            bgfx::setViewRect(RENDER_PASS_SHADOW_MAP, 0, 0, SHADOW_MAP_DIM, SHADOW_MAP_DIM);
+
+            drawAllModels(RENDER_PASS_SHADOW_MAP, m_shadowProgram);
+            
+            // Next draw light buffer
+
+            // Set up matrices for light buffer
+            bgfx::setViewRect(RENDER_PASS_LIGHT_BUFFER, 0, 0, uint16_t(m_width), uint16_t(m_height));
+            bgfx::setViewTransform(RENDER_PASS_LIGHT_BUFFER, view, proj);  // Notice, same view and proj as gbuffer
+            // Set drawing into light buffer
+            bgfx::setViewFrameBuffer(RENDER_PASS_LIGHT_BUFFER, m_lightBuffer);
+
+            // Inverse view projection is needed in shader so set that up
+            float vp[16], invMvp[16];
+            bx::mtxMul(vp, view, proj);
+            bx::mtxInverse(invMvp, vp);
+
+            // Light matrix used in combine pass and inverse used in light pass
+            float lightMtx[16]; // World space to light space (shadow map space)
+            bx::mtxMul(lightMtx, smView, smProj);
+            float invMvpShadow[16];
+            bx::mtxInverse(invMvpShadow, lightMtx);
+
+            // Draw some lights (these should really be instanced but for this example they aren't...)
+            const unsigned MAX_SPHERE = 32;
+            for (uint32_t i = 0; i < MAX_SPHERE; i++) {
+                for (uint32_t j = 0; j < MAX_SPHERE; j++) {
+                    // These are used in the fragment shader
+                    bgfx::setTexture(0, s_normal, m_gbuffer, GBUFFER_RT_NORMAL);  // Normal for lighting calculations
+                    bgfx::setTexture(1, s_depth,  m_gbuffer, GBUFFER_RT_DEPTH);   // Depth to reconstruct world position
+
+                    // Thse are used in the vert shader
+                    bgfx::setTexture(2, s_shadowMap, m_shadowBuffer, SHADOW_RT_DEPTH);  // Used to place sphere
+                    bgfx::setTexture(3, s_rsm,       m_shadowBuffer, SHADOW_RT_RSM);    // Used to scale/color sphere
+
+                    bgfx::setUniform(u_invMvp, invMvp);
+                    bgfx::setUniform(u_invMvpShadow, invMvpShadow);
+                    float sphereInfo[4];
+                    sphereInfo[0] = ((float)i/(MAX_SPHERE-1));
+                    sphereInfo[1] = ((float)j/(MAX_SPHERE-1));
+                    sphereInfo[2] = m_vplRadius;
+                    sphereInfo[3] = 0.0;  // Unused
+                    bgfx::setUniform(u_sphereInfo, sphereInfo);
+
+                    const uint64_t lightDrawState = 0
+                        | BGFX_STATE_RGB_WRITE
+                        | BGFX_STATE_BLEND_ADD   // <===  Overlapping lights contribute more
+                        | BGFX_STATE_ALPHA_WRITE
+                        | BGFX_STATE_CULL_CW     // <===  If we go into the lights, there will be problems, so we draw the far back face.
+                        ;
+
+                    meshSubmit(
+                        m_lightSphere,
+                        RENDER_PASS_LIGHT_BUFFER,
+                        m_lightProgram,
+                        NULL,
+                        lightDrawState
+                        );
+                }
+            }
+            
+            // Draw combine pass
+
+            // Texture inputs for combine pass
+            bgfx::setTexture(0, s_normal, m_gbuffer, GBUFFER_RT_NORMAL);
+            bgfx::setTexture(1, s_color, m_gbuffer, GBUFFER_RT_COLOR);  
+            bgfx::setTexture(2, s_light, m_lightBuffer, 0);
+            bgfx::setTexture(3, s_depth, m_gbuffer, GBUFFER_RT_DEPTH);
+            bgfx::setTexture(4, s_shadowMap, m_shadowBuffer, SHADOW_RT_DEPTH, BGFX_TEXTURE_COMPARE_LEQUAL);
+
+            // Uniforms for combine pass
+
+            bgfx::setUniform(u_lightDir, m_lightDir);
+            bgfx::setUniform(u_invMvp, invMvp);
+            bgfx::setUniform(u_lightMtx, lightMtx);
+            const float invDim[4] = {1.0f/SHADOW_MAP_DIM, 0.0f, 0.0f, 0.0f};
+            bgfx::setUniform(u_shadowDimsInv, invDim);
+            float rsmAmount[4] = {m_rsmAmount,m_rsmAmount,m_rsmAmount,m_rsmAmount};
+            bgfx::setUniform(u_rsmAmount, rsmAmount);
+
+            // Set up state for combine pass
+            // point of this is to avoid doing depth test, which is in the default state
+            bgfx::setState(0
+                | BGFX_STATE_RGB_WRITE
+                | BGFX_STATE_ALPHA_WRITE
+                );
+
+            // Set up transform matrix for fullscreen quad
+            float orthoProj[16];
+            bx::mtxOrtho(orthoProj, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 100.0f);
+            bgfx::setViewTransform(RENDER_PASS_COMBINE,   NULL, orthoProj);
+            bgfx::setViewRect(RENDER_PASS_COMBINE, 0, 0, m_width, m_height);
+            // Bind vertex buffer and draw quad
+            screenSpaceQuad( (float)m_width, (float)m_height, m_texelHalf, m_caps->originBottomLeft);
+            bgfx::submit(RENDER_PASS_COMBINE, m_combineProgram);
+
+            // Draw UI
+            imguiBeginFrame(m_mouseState.m_mx
+                , m_mouseState.m_my
+                , (m_mouseState.m_buttons[entry::MouseButton::Left] ? IMGUI_MBUT_LEFT : 0)
+                | (m_mouseState.m_buttons[entry::MouseButton::Right] ? IMGUI_MBUT_RIGHT : 0)
+                | (m_mouseState.m_buttons[entry::MouseButton::Middle] ? IMGUI_MBUT_MIDDLE : 0)
+                , m_mouseState.m_mz
+                , m_width
+                , m_height
+                );
+
+            imguiBeginArea("RSM:", 10, 100, 300, 400);
+
+            imguiSlider("rsm amount", m_rsmAmount, 0.0f, 0.7f, 0.01f);
+            imguiSlider("vpl radius", m_vplRadius, 0.25f, 20.0f, 0.1f);
+            imguiSlider("light azimuth", m_lightAzimuth, 0.0f, 360.0f, 0.01f);
+            imguiSlider("light elevation", m_lightElevation, 35.0f, 90.0f, 0.01f);
+            
+            imguiEndArea();
+            imguiEndFrame();
+
+            updateLightDir();
+
+			// Advance to next frame. Rendering thread will be kicked to
+			// process submitted rendering primitives.
+			m_currFrame = bgfx::frame();
+
+			return true;
+		}
+
+		return false;
+	}
+
+    void drawAllModels(uint8_t _pass, bgfx::ProgramHandle _program) {
+        for (const Model & m : m_models) {
+            // Set up transform matrix for each model
+            float scale = m_meshScale[m.mesh];
+            float mtx[16];
+            bx::mtxSRT(mtx
+                , scale
+                , scale
+                , scale
+                , 0.0f
+                , 0.0f
+                , 0.0f
+                , m.position[0]
+                , m.position[1]
+                , m.position[2]
+                );
+            
+            // Submit mesh to gbuffer
+            bgfx::setUniform(u_tint, m.color);
+            meshSubmit(m_meshes[m.mesh], _pass, _program, mtx);
+        }
+
+        // Draw ground
+        const float white[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+        bgfx::setUniform(u_tint, white);
+        float mtxScale[16];
+        float scale = 10.0;
+        bx::mtxScale(mtxScale
+            , scale
+            , scale
+            , scale
+            );
+        float mtxTrans[16];
+        bx::mtxTranslate(mtxTrans
+            , 0.0f
+            , -10.0f
+            , 0.0f
+            );
+        float mtx[16];
+        bx::mtxMul(mtx, mtxScale, mtxTrans);
+        meshSubmit(m_ground, _pass, _program, mtx);
+    }
+
+    void updateLightDir () {
+        float el = m_lightElevation * (bx::pi/180.0f);
+        float az = m_lightAzimuth * (bx::pi/180.0f);
+        m_lightDir[0] = cos(el)*cos(az);
+        m_lightDir[2] = cos(el)*sin(az);
+        m_lightDir[1] = sin(el);
+        m_lightDir[3] = 0.0f;
+    }
+
+	uint32_t m_width;
+	uint32_t m_height;
+	uint32_t m_debug;
+	uint32_t m_reset;
+
+    entry::MouseState m_mouseState;
+	
+    Mesh* m_ground;
+    Mesh* m_lightSphere; // Unit sphere
+    
+    // Resource handles
+	bgfx::ProgramHandle m_gbufferProgram;
+    bgfx::ProgramHandle m_shadowProgram;
+    bgfx::ProgramHandle m_lightProgram;
+    bgfx::ProgramHandle m_combineProgram;
+    bgfx::FrameBufferHandle m_gbuffer;
+    bgfx::FrameBufferHandle m_lightBuffer;
+    bgfx::FrameBufferHandle m_shadowBuffer;
+
+    // Shader uniforms
+    bgfx::UniformHandle u_tint;
+    bgfx::UniformHandle u_invMvp;
+    bgfx::UniformHandle u_invMvpShadow;
+    bgfx::UniformHandle u_lightMtx;
+    bgfx::UniformHandle u_lightDir;
+    bgfx::UniformHandle u_sphereInfo;
+    bgfx::UniformHandle u_shadowDimsInv;
+    bgfx::UniformHandle u_rsmAmount;
+
+    // Uniforms to identify texture samples
+    bgfx::UniformHandle s_normal;
+    bgfx::UniformHandle s_depth;
+    bgfx::UniformHandle s_color;
+    bgfx::UniformHandle s_light;
+    bgfx::UniformHandle s_shadowMap;
+    bgfx::UniformHandle s_rsm;
+
+    // Various render targets
+    bgfx::TextureHandle m_gbufferTex[3];
+    bgfx::TextureHandle m_lightBufferTex;
+    bgfx::TextureHandle m_shadowBufferTex[2];
+
+    uint32_t m_reading = 0;
+    uint32_t m_currFrame = UINT32_MAX;
+
+    // UI 
+    bool m_cameraSpin = false;
+
+    struct Model {
+        uint32_t mesh; // Index of mesh in m_meshes
+        float color[4];
+        float position[3];
+    };
+
+    Model m_models[MODEL_COUNT];
+
+    const char * m_meshPaths[MESH_COUNT] = {
+        "meshes/cube.bin"
+        ,"meshes/orb.bin"
+        ,"meshes/column.bin"
+        ,"meshes/bunny.bin"
+        ,"meshes/tree.bin"
+        ,"meshes/hollowcube.bin"
+    };
+
+    // Light position;
+    float m_lightDir[4];
+    float m_lightElevation = 35.0f;
+    float m_lightAzimuth = 215.0f;
+
+    
+    float m_rsmAmount = 0.25f; // Amount of rsm
+    float m_vplRadius = 3.0f;  // Radius of virtual point light
+
+    const float m_meshScale[MESH_COUNT] = {0.25f, 0.5f, 0.05f, 0.5f, 0.05f, 0.05f};
+    const bgfx::Caps* m_caps;
+    Mesh * m_meshes[MESH_COUNT];
+    
+    float m_texelHalf = 0.0f;  // Texel offset for dx9
+};
+
+ENTRY_IMPLEMENT_MAIN(ExampleReflectiveShadowMap);

BIN
examples/31-reflectiveshadowmap/screenshot.png


+ 10 - 0
examples/31-reflectiveshadowmap/varying.def.sc

@@ -0,0 +1,10 @@
+vec4 v_color0    : COLOR0    = vec4(1.0, 0.0, 0.0, 1.0);
+vec3 v_normal    : NORMAL    = vec3(0.0, 0.0, 1.0);
+vec2 v_texcoord0 : TEXCOORD0 = vec2(0.0, 0.0);
+vec3 v_view      : TEXCOORD1 = vec3(0.0, 0.0, 0.0);
+vec4 v_lightCenterScale : TEXCOORD2 = vec4(0.0, 0.0, 0.0, 0.0);  // xyz is position z is scale
+
+vec3 a_position  : POSITION;
+vec4 a_color0    : COLOR0;
+vec2 a_texcoord0 : TEXCOORD0;
+vec3 a_normal    : NORMAL;

+ 15 - 0
examples/31-reflectiveshadowmap/vs_rsm_combine.sc

@@ -0,0 +1,15 @@
+$input a_position, a_texcoord0
+$output v_texcoord0
+
+/*
+ * Copyright 2016 Joseph Cherlin. All rights reserved.
+ * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
+ */
+
+#include "../common/common.sh"
+
+void main()
+{
+	gl_Position = mul(u_modelViewProj, vec4(a_position, 1.0) );
+	v_texcoord0 = a_texcoord0;
+}

+ 27 - 0
examples/31-reflectiveshadowmap/vs_rsm_gbuffer.sc

@@ -0,0 +1,27 @@
+$input a_position, a_normal
+$output v_normal
+
+/*
+* Copyright 2016 Joseph Cherlin. All rights reserved.
+* License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
+*/
+
+
+#include "../common/common.sh"
+
+uniform vec4 u_tint;
+
+void main()
+{
+    // Calculate vertex position
+	vec3 pos = a_position;
+    gl_Position = mul(u_modelViewProj, vec4(pos, 1.0) );
+
+    // Calculate normal.  Note that compressed normal is stored in the vertices
+	vec3 normalObjectSpace = a_normal.xyz*2.0+-1.0; // Normal is stored in [0,1], remap to [-1,1].
+    
+    // Transform normal into world space.  
+    vec3 normalWorldSpace = mul(u_model[0], vec4(normalObjectSpace, 0.0) ).xyz;
+    // Normalize to remove (uniform...) scaling, however, recompress
+    v_normal.xyz = normalize(normalWorldSpace)*0.5+0.5;
+}

+ 60 - 0
examples/31-reflectiveshadowmap/vs_rsm_lbuffer.sc

@@ -0,0 +1,60 @@
+$input a_position
+$output v_lightCenterScale, v_color0
+
+/*
+ * Copyright 2016 Joseph Cherlin. All rights reserved.
+ * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
+ */
+
+#include "../common/common.sh"
+
+uniform vec4 u_sphereInfo;
+uniform mat4 u_invMvpShadow;
+
+
+// Note texture binding starts at slot 2.  Problem is that vert textures and frag textures are different.
+SAMPLER2D(s_shadowMap, 2);  // Used to reconstruct 3d position for lights
+SAMPLER2D(s_rsm,       3);  // Reflective shadow map, used to scale/color light
+
+float toClipSpaceDepth(float _depthTextureZ)
+{
+#if BGFX_SHADER_LANGUAGE_HLSL || BGFX_SHADER_LANGUAGE_METAL
+    return _depthTextureZ;
+#else
+    return _depthTextureZ * 2.0 - 1.0;
+#endif // BGFX_SHADER_LANGUAGE_HLSL || BGFX_SHADER_LANGUAGE_METAL
+}
+
+vec3 clipToWorld(mat4 _invViewProj, vec3 _clipPos)
+{
+    vec4 wpos = mul(_invViewProj, vec4(_clipPos, 1.0) );
+    return wpos.xyz / wpos.w;
+}
+
+void main()
+{
+    // Calculate vertex position
+	vec3 objectSpacePos = a_position;
+    vec2 texCoord       = u_sphereInfo.xy;
+
+    // Get world position using the shadow map
+    float deviceDepth = texture2DLod(s_shadowMap, texCoord, 0).x;
+    float depth       = toClipSpaceDepth(deviceDepth);
+    vec3 clip = vec3(texCoord * 2.0 - 1.0, depth);
+#if BGFX_SHADER_LANGUAGE_HLSL || BGFX_SHADER_LANGUAGE_METAL
+    clip.y = -clip.y;
+#endif // BGFX_SHADER_LANGUAGE_HLSL || BGFX_SHADER_LANGUAGE_METAL
+    vec3 wPos = clipToWorld(u_invMvpShadow, clip);
+    wPos.y -= 0.001;  // Would be much better to perturb in normal direction, but I didn't do that.
+
+    // Scale and color are already in the rsm
+    vec4 rsm = texture2DLod(s_rsm, texCoord, 0).xyzw;
+    float radScale = u_sphereInfo.z;
+    float rad = rsm.w * radScale;
+
+    gl_Position = mul(u_viewProj, vec4(wPos+objectSpacePos*rad, 1.0) );
+    
+    v_lightCenterScale.xyz = wPos.xyz;
+    v_lightCenterScale.w = rad;
+    v_color0.xyz = rsm.xyz;
+}

+ 24 - 0
examples/31-reflectiveshadowmap/vs_rsm_shadow.sc

@@ -0,0 +1,24 @@
+$input a_position, a_normal
+$output v_normal // RSM shadow
+
+/*
+ * Copyright 2016 Joseph Cherlin. All rights reserved.
+ * License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause
+ */
+
+#include "../common/common.sh"
+
+uniform vec4 u_tint;
+
+void main()
+{
+	gl_Position = mul(u_modelViewProj, vec4(a_position, 1.0) );
+    
+    // Calculate normal.  Note that compressed normal is stored in the vertices
+    vec3 normalObjectSpace = a_normal.xyz*2.0+-1.0; // Normal is stored in [0,1], remap to [-1,1].
+    
+    // Transform normal into view space.  
+    v_normal = mul(u_modelView, vec4(normalObjectSpace, 0.0) ).xyz;
+    // Normalize to remove (uniform...) scaling
+    v_normal = normalize(v_normal);
+}

BIN
examples/runtime/meshes/unit_sphere.bin


+ 1 - 0
scripts/genie.lua

@@ -392,6 +392,7 @@ exampleProject("27-terrain")
 exampleProject("28-wireframe")
 exampleProject("28-wireframe")
 exampleProject("29-debugdraw")
 exampleProject("29-debugdraw")
 exampleProject("30-picking")
 exampleProject("30-picking")
+exampleProject("31-reflectiveshadowmap")
 
 
 -- C99 source doesn't compile under WinRT settings
 -- C99 source doesn't compile under WinRT settings
 if not premake.vstudio.iswinrt() then
 if not premake.vstudio.iswinrt() then