Ver Fonte

Feature: Various improvements and fixes for shadow rendering
- Shadow rendering now always renders backfaces in order to reduce shadow acne
- Shadow bias was tweaked for all shadow types to reduce floating shadows when using default scale
- Shadow bias parameter exposed in the editor under Light's Inspector
- Fixed incorrect frustum culling when rendering omnidirectinal shadows

BearishSun há 8 anos atrás
pai
commit
517608073c

BIN
Data/Engine/Includes/ShadowDepthBase.bslinc.asset


BIN
Data/Engine/Shaders/ShadowDepthCube.bsl.asset


BIN
Data/Engine/Shaders/ShadowDepthDirectional.bsl.asset


BIN
Data/Engine/Shaders/ShadowDepthNormal.bsl.asset


BIN
Data/Engine/Shaders/ShadowProjectOmni.bsl.asset


+ 6 - 0
Data/Raw/Engine/Includes/ShadowDepthBase.bslinc

@@ -6,6 +6,12 @@ mixin ShadowDepthBase
 	mixin PerObjectData;
 	mixin VertexInput;
 
+	// Render back-faces to reduce shadow acne (assuming thick geometry everywhere)
+	raster
+	{
+		cull = cw;
+	};	
+	
 	code
 	{
 		struct ShadowVStoFS

+ 3 - 1
Data/Raw/Engine/Shaders/ShadowDepthCube.bsl

@@ -52,8 +52,10 @@ technique ShadowDepth
 						GSToPS output;
 						output.targetIdx = faceIdx;
 
+						// Note: I'm reversing the order here because otherwise the triangle order is reversed
+						// (not sure why, but GS seems like it could be the only culprit)
 						[unroll]
-						for (int vertIdx = 0; vertIdx < 3; vertIdx++)
+						for (int vertIdx = 2; vertIdx >= 0; vertIdx--)
 						{
 							output.position = clipPos[vertIdx];
 							outStream.Append(output);

+ 8 - 5
Source/BansheeCore/Renderer/BsLight.h

@@ -56,13 +56,16 @@ namespace bs
 		bool getCastsShadow() const { return mCastsShadows; }
 
 		/** 
-		 * Shadow bias determines shadow accuracy. Low bias values mean that shadows start closer near their caster surface
-		 * but will result in more shadowing artifacts (shadow acne). Larger values reduce shadow acne but caster may appear
-		 * as floating on air as nearby part of the shadow is cut off (peter paning).
+		 * Shadow bias determines offset at which the shadows are rendered from the shadow caster. Bias value of 0 means
+		 * the shadow will be renderered exactly at the casters position. If your geometry has thin areas this will
+		 * produce an artifact called shadow acne, in which case you can increase the shadow bias value to eliminate it. 
+		 * Note that increasing the shadow bias will on the other hand make the shadow be offset from the caster and may
+		 * make the caster appear as if floating (Peter Panning artifact). Neither is perfect, so it is preferable to ensure
+		 * all your geometry has thickness and keep the bias at zero, or even at negative values.
 		 * 
-		 * Default value is 0.5. Must be in range [0, 1].
+		 * Default value is 0.5. Should be in rough range [-1, 1].
 		 */
-		void setShadowBias(float bias) { mShadowBias = std::max(std::min(bias, 1.0f), 0.0f); _markCoreDirty(); }
+		void setShadowBias(float bias) { mShadowBias = bias; _markCoreDirty(); }
 
 		/** @copydoc setShadowBias() */
 		float getShadowBias() const { return mShadowBias; }

+ 185 - 185
Source/BansheeUtility/Math/BsMatrix4.cpp

@@ -8,56 +8,56 @@
 
 namespace bs
 {
-    const Matrix4 Matrix4::ZERO(
-        0.0f, 0.0f, 0.0f, 0.0f,
-        0.0f, 0.0f, 0.0f, 0.0f,
-        0.0f, 0.0f, 0.0f, 0.0f,
-        0.0f, 0.0f, 0.0f, 0.0f);
+	const Matrix4 Matrix4::ZERO(
+		0.0f, 0.0f, 0.0f, 0.0f,
+		0.0f, 0.0f, 0.0f, 0.0f,
+		0.0f, 0.0f, 0.0f, 0.0f,
+		0.0f, 0.0f, 0.0f, 0.0f);
 
-    const Matrix4 Matrix4::IDENTITY(
+	const Matrix4 Matrix4::IDENTITY(
 		1.0f, 0.0f, 0.0f, 0.0f,
 		0.0f, 1.0f, 0.0f, 0.0f,
 		0.0f, 0.0f, 1.0f, 0.0f,
 		0.0f, 0.0f, 0.0f, 1.0f);
 
-    static float MINOR(const Matrix4& m, const UINT32 r0, const UINT32 r1, const UINT32 r2, 
+	static float MINOR(const Matrix4& m, const UINT32 r0, const UINT32 r1, const UINT32 r2, 
 								const UINT32 c0, const UINT32 c1, const UINT32 c2)
-    {
-        return m[r0][c0] * (m[r1][c1] * m[r2][c2] - m[r2][c1] * m[r1][c2]) -
-            m[r0][c1] * (m[r1][c0] * m[r2][c2] - m[r2][c0] * m[r1][c2]) +
-            m[r0][c2] * (m[r1][c0] * m[r2][c1] - m[r2][c0] * m[r1][c1]);
-    }
-
-    Matrix4 Matrix4::adjoint() const
-    {
-        return Matrix4( MINOR(*this, 1, 2, 3, 1, 2, 3),
-            -MINOR(*this, 0, 2, 3, 1, 2, 3),
-            MINOR(*this, 0, 1, 3, 1, 2, 3),
-            -MINOR(*this, 0, 1, 2, 1, 2, 3),
-
-            -MINOR(*this, 1, 2, 3, 0, 2, 3),
-            MINOR(*this, 0, 2, 3, 0, 2, 3),
-            -MINOR(*this, 0, 1, 3, 0, 2, 3),
-            MINOR(*this, 0, 1, 2, 0, 2, 3),
-
-            MINOR(*this, 1, 2, 3, 0, 1, 3),
-            -MINOR(*this, 0, 2, 3, 0, 1, 3),
-            MINOR(*this, 0, 1, 3, 0, 1, 3),
-            -MINOR(*this, 0, 1, 2, 0, 1, 3),
-
-            -MINOR(*this, 1, 2, 3, 0, 1, 2),
-            MINOR(*this, 0, 2, 3, 0, 1, 2),
-            -MINOR(*this, 0, 1, 3, 0, 1, 2),
-            MINOR(*this, 0, 1, 2, 0, 1, 2));
-    }
-
-    float Matrix4::determinant() const
-    {
-        return m[0][0] * MINOR(*this, 1, 2, 3, 1, 2, 3) -
-            m[0][1] * MINOR(*this, 1, 2, 3, 0, 2, 3) +
-            m[0][2] * MINOR(*this, 1, 2, 3, 0, 1, 3) -
-            m[0][3] * MINOR(*this, 1, 2, 3, 0, 1, 2);
-    }
+	{
+		return m[r0][c0] * (m[r1][c1] * m[r2][c2] - m[r2][c1] * m[r1][c2]) -
+			m[r0][c1] * (m[r1][c0] * m[r2][c2] - m[r2][c0] * m[r1][c2]) +
+			m[r0][c2] * (m[r1][c0] * m[r2][c1] - m[r2][c0] * m[r1][c1]);
+	}
+
+	Matrix4 Matrix4::adjoint() const
+	{
+		return Matrix4( MINOR(*this, 1, 2, 3, 1, 2, 3),
+			-MINOR(*this, 0, 2, 3, 1, 2, 3),
+			MINOR(*this, 0, 1, 3, 1, 2, 3),
+			-MINOR(*this, 0, 1, 2, 1, 2, 3),
+
+			-MINOR(*this, 1, 2, 3, 0, 2, 3),
+			MINOR(*this, 0, 2, 3, 0, 2, 3),
+			-MINOR(*this, 0, 1, 3, 0, 2, 3),
+			MINOR(*this, 0, 1, 2, 0, 2, 3),
+
+			MINOR(*this, 1, 2, 3, 0, 1, 3),
+			-MINOR(*this, 0, 2, 3, 0, 1, 3),
+			MINOR(*this, 0, 1, 3, 0, 1, 3),
+			-MINOR(*this, 0, 1, 2, 0, 1, 3),
+
+			-MINOR(*this, 1, 2, 3, 0, 1, 2),
+			MINOR(*this, 0, 2, 3, 0, 1, 2),
+			-MINOR(*this, 0, 1, 3, 0, 1, 2),
+			MINOR(*this, 0, 1, 2, 0, 1, 2));
+	}
+
+	float Matrix4::determinant() const
+	{
+		return m[0][0] * MINOR(*this, 1, 2, 3, 1, 2, 3) -
+			m[0][1] * MINOR(*this, 1, 2, 3, 0, 2, 3) +
+			m[0][2] * MINOR(*this, 1, 2, 3, 0, 1, 3) -
+			m[0][3] * MINOR(*this, 1, 2, 3, 0, 1, 2);
+	}
 
 	float Matrix4::determinant3x3() const
 	{
@@ -70,149 +70,149 @@ namespace bs
 		return det;
 	}
 
-    Matrix4 Matrix4::inverse() const
-    {
-        float m00 = m[0][0], m01 = m[0][1], m02 = m[0][2], m03 = m[0][3];
-        float m10 = m[1][0], m11 = m[1][1], m12 = m[1][2], m13 = m[1][3];
-        float m20 = m[2][0], m21 = m[2][1], m22 = m[2][2], m23 = m[2][3];
-        float m30 = m[3][0], m31 = m[3][1], m32 = m[3][2], m33 = m[3][3];
-
-        float v0 = m20 * m31 - m21 * m30;
-        float v1 = m20 * m32 - m22 * m30;
-        float v2 = m20 * m33 - m23 * m30;
-        float v3 = m21 * m32 - m22 * m31;
-        float v4 = m21 * m33 - m23 * m31;
-        float v5 = m22 * m33 - m23 * m32;
-
-        float t00 = + (v5 * m11 - v4 * m12 + v3 * m13);
-        float t10 = - (v5 * m10 - v2 * m12 + v1 * m13);
-        float t20 = + (v4 * m10 - v2 * m11 + v0 * m13);
-        float t30 = - (v3 * m10 - v1 * m11 + v0 * m12);
-
-        float invDet = 1 / (t00 * m00 + t10 * m01 + t20 * m02 + t30 * m03);
-
-        float d00 = t00 * invDet;
-        float d10 = t10 * invDet;
-        float d20 = t20 * invDet;
-        float d30 = t30 * invDet;
-
-        float d01 = - (v5 * m01 - v4 * m02 + v3 * m03) * invDet;
-        float d11 = + (v5 * m00 - v2 * m02 + v1 * m03) * invDet;
-        float d21 = - (v4 * m00 - v2 * m01 + v0 * m03) * invDet;
-        float d31 = + (v3 * m00 - v1 * m01 + v0 * m02) * invDet;
-
-        v0 = m10 * m31 - m11 * m30;
-        v1 = m10 * m32 - m12 * m30;
-        v2 = m10 * m33 - m13 * m30;
-        v3 = m11 * m32 - m12 * m31;
-        v4 = m11 * m33 - m13 * m31;
-        v5 = m12 * m33 - m13 * m32;
-
-        float d02 = + (v5 * m01 - v4 * m02 + v3 * m03) * invDet;
-        float d12 = - (v5 * m00 - v2 * m02 + v1 * m03) * invDet;
-        float d22 = + (v4 * m00 - v2 * m01 + v0 * m03) * invDet;
-        float d32 = - (v3 * m00 - v1 * m01 + v0 * m02) * invDet;
-
-        v0 = m21 * m10 - m20 * m11;
-        v1 = m22 * m10 - m20 * m12;
-        v2 = m23 * m10 - m20 * m13;
-        v3 = m22 * m11 - m21 * m12;
-        v4 = m23 * m11 - m21 * m13;
-        v5 = m23 * m12 - m22 * m13;
-
-        float d03 = - (v5 * m01 - v4 * m02 + v3 * m03) * invDet;
-        float d13 = + (v5 * m00 - v2 * m02 + v1 * m03) * invDet;
-        float d23 = - (v4 * m00 - v2 * m01 + v0 * m03) * invDet;
-        float d33 = + (v3 * m00 - v1 * m01 + v0 * m02) * invDet;
+	Matrix4 Matrix4::inverse() const
+	{
+		float m00 = m[0][0], m01 = m[0][1], m02 = m[0][2], m03 = m[0][3];
+		float m10 = m[1][0], m11 = m[1][1], m12 = m[1][2], m13 = m[1][3];
+		float m20 = m[2][0], m21 = m[2][1], m22 = m[2][2], m23 = m[2][3];
+		float m30 = m[3][0], m31 = m[3][1], m32 = m[3][2], m33 = m[3][3];
+
+		float v0 = m20 * m31 - m21 * m30;
+		float v1 = m20 * m32 - m22 * m30;
+		float v2 = m20 * m33 - m23 * m30;
+		float v3 = m21 * m32 - m22 * m31;
+		float v4 = m21 * m33 - m23 * m31;
+		float v5 = m22 * m33 - m23 * m32;
+
+		float t00 = + (v5 * m11 - v4 * m12 + v3 * m13);
+		float t10 = - (v5 * m10 - v2 * m12 + v1 * m13);
+		float t20 = + (v4 * m10 - v2 * m11 + v0 * m13);
+		float t30 = - (v3 * m10 - v1 * m11 + v0 * m12);
+
+		float invDet = 1 / (t00 * m00 + t10 * m01 + t20 * m02 + t30 * m03);
+
+		float d00 = t00 * invDet;
+		float d10 = t10 * invDet;
+		float d20 = t20 * invDet;
+		float d30 = t30 * invDet;
+
+		float d01 = - (v5 * m01 - v4 * m02 + v3 * m03) * invDet;
+		float d11 = + (v5 * m00 - v2 * m02 + v1 * m03) * invDet;
+		float d21 = - (v4 * m00 - v2 * m01 + v0 * m03) * invDet;
+		float d31 = + (v3 * m00 - v1 * m01 + v0 * m02) * invDet;
+
+		v0 = m10 * m31 - m11 * m30;
+		v1 = m10 * m32 - m12 * m30;
+		v2 = m10 * m33 - m13 * m30;
+		v3 = m11 * m32 - m12 * m31;
+		v4 = m11 * m33 - m13 * m31;
+		v5 = m12 * m33 - m13 * m32;
+
+		float d02 = + (v5 * m01 - v4 * m02 + v3 * m03) * invDet;
+		float d12 = - (v5 * m00 - v2 * m02 + v1 * m03) * invDet;
+		float d22 = + (v4 * m00 - v2 * m01 + v0 * m03) * invDet;
+		float d32 = - (v3 * m00 - v1 * m01 + v0 * m02) * invDet;
+
+		v0 = m21 * m10 - m20 * m11;
+		v1 = m22 * m10 - m20 * m12;
+		v2 = m23 * m10 - m20 * m13;
+		v3 = m22 * m11 - m21 * m12;
+		v4 = m23 * m11 - m21 * m13;
+		v5 = m23 * m12 - m22 * m13;
+
+		float d03 = - (v5 * m01 - v4 * m02 + v3 * m03) * invDet;
+		float d13 = + (v5 * m00 - v2 * m02 + v1 * m03) * invDet;
+		float d23 = - (v4 * m00 - v2 * m01 + v0 * m03) * invDet;
+		float d33 = + (v3 * m00 - v1 * m01 + v0 * m02) * invDet;
 		
-        return Matrix4(
-            d00, d01, d02, d03,
-            d10, d11, d12, d13,
-            d20, d21, d22, d23,
-            d30, d31, d32, d33);
-    }
-
-    Matrix4 Matrix4::inverseAffine() const
-    {
-        BS_ASSERT(isAffine());
-
-        float m10 = m[1][0], m11 = m[1][1], m12 = m[1][2];
-        float m20 = m[2][0], m21 = m[2][1], m22 = m[2][2];
-
-        float t00 = m22 * m11 - m21 * m12;
-        float t10 = m20 * m12 - m22 * m10;
-        float t20 = m21 * m10 - m20 * m11;
-
-        float m00 = m[0][0], m01 = m[0][1], m02 = m[0][2];
-
-        float invDet = 1 / (m00 * t00 + m01 * t10 + m02 * t20);
-
-        t00 *= invDet; t10 *= invDet; t20 *= invDet;
-
-        m00 *= invDet; m01 *= invDet; m02 *= invDet;
-
-        float r00 = t00;
-        float r01 = m02 * m21 - m01 * m22;
-        float r02 = m01 * m12 - m02 * m11;
-
-        float r10 = t10;
-        float r11 = m00 * m22 - m02 * m20;
-        float r12 = m02 * m10 - m00 * m12;
-
-        float r20 = t20;
-        float r21 = m01 * m20 - m00 * m21;
-        float r22 = m00 * m11 - m01 * m10;
-
-        float m03 = m[0][3], m13 = m[1][3], m23 = m[2][3];
-
-        float r03 = - (r00 * m03 + r01 * m13 + r02 * m23);
-        float r13 = - (r10 * m03 + r11 * m13 + r12 * m23);
-        float r23 = - (r20 * m03 + r21 * m13 + r22 * m23);
-
-        return Matrix4(
-            r00, r01, r02, r03,
-            r10, r11, r12, r13,
-            r20, r21, r22, r23,
-              0,   0,   0,   1);
-    }
-
-    void Matrix4::setTRS(const Vector3& translation, const Quaternion& rotation, const Vector3& scale)
-    {
-        Matrix3 rot3x3;
-        rotation.toRotationMatrix(rot3x3);
-
-        m[0][0] = scale.x * rot3x3[0][0]; m[0][1] = scale.y * rot3x3[0][1]; m[0][2] = scale.z * rot3x3[0][2]; m[0][3] = translation.x;
-        m[1][0] = scale.x * rot3x3[1][0]; m[1][1] = scale.y * rot3x3[1][1]; m[1][2] = scale.z * rot3x3[1][2]; m[1][3] = translation.y;
-        m[2][0] = scale.x * rot3x3[2][0]; m[2][1] = scale.y * rot3x3[2][1]; m[2][2] = scale.z * rot3x3[2][2]; m[2][3] = translation.z;
-
-        // No projection term
-        m[3][0] = 0; m[3][1] = 0; m[3][2] = 0; m[3][3] = 1;
-    }
-
-    void Matrix4::setInverseTRS(const Vector3& translation, const Quaternion& rotation, const Vector3& scale)
-    {
-        // Invert the parameters
-        Vector3 invTranslate = -translation;
-        Vector3 invScale(1 / scale.x, 1 / scale.y, 1 / scale.z);
-        Quaternion invRot = rotation.inverse();
-
-        // Because we're inverting, order is translation, rotation, scale
-        // So make translation relative to scale & rotation
-        invTranslate = invRot.rotate(invTranslate);
-        invTranslate *= invScale;
-
-        // Next, make a 3x3 rotation matrix
-        Matrix3 rot3x3;
-        invRot.toRotationMatrix(rot3x3);
-
-        // Set up final matrix with scale, rotation and translation
-        m[0][0] = invScale.x * rot3x3[0][0]; m[0][1] = invScale.x * rot3x3[0][1]; m[0][2] = invScale.x * rot3x3[0][2]; m[0][3] = invTranslate.x;
-        m[1][0] = invScale.y * rot3x3[1][0]; m[1][1] = invScale.y * rot3x3[1][1]; m[1][2] = invScale.y * rot3x3[1][2]; m[1][3] = invTranslate.y;
-        m[2][0] = invScale.z * rot3x3[2][0]; m[2][1] = invScale.z * rot3x3[2][1]; m[2][2] = invScale.z * rot3x3[2][2]; m[2][3] = invTranslate.z;		
-
-        // No projection term
-        m[3][0] = 0; m[3][1] = 0; m[3][2] = 0; m[3][3] = 1;
-    }
+		return Matrix4(
+			d00, d01, d02, d03,
+			d10, d11, d12, d13,
+			d20, d21, d22, d23,
+			d30, d31, d32, d33);
+	}
+
+	Matrix4 Matrix4::inverseAffine() const
+	{
+		BS_ASSERT(isAffine());
+
+		float m10 = m[1][0], m11 = m[1][1], m12 = m[1][2];
+		float m20 = m[2][0], m21 = m[2][1], m22 = m[2][2];
+
+		float t00 = m22 * m11 - m21 * m12;
+		float t10 = m20 * m12 - m22 * m10;
+		float t20 = m21 * m10 - m20 * m11;
+
+		float m00 = m[0][0], m01 = m[0][1], m02 = m[0][2];
+
+		float invDet = 1 / (m00 * t00 + m01 * t10 + m02 * t20);
+
+		t00 *= invDet; t10 *= invDet; t20 *= invDet;
+
+		m00 *= invDet; m01 *= invDet; m02 *= invDet;
+
+		float r00 = t00;
+		float r01 = m02 * m21 - m01 * m22;
+		float r02 = m01 * m12 - m02 * m11;
+
+		float r10 = t10;
+		float r11 = m00 * m22 - m02 * m20;
+		float r12 = m02 * m10 - m00 * m12;
+
+		float r20 = t20;
+		float r21 = m01 * m20 - m00 * m21;
+		float r22 = m00 * m11 - m01 * m10;
+
+		float m03 = m[0][3], m13 = m[1][3], m23 = m[2][3];
+
+		float r03 = - (r00 * m03 + r01 * m13 + r02 * m23);
+		float r13 = - (r10 * m03 + r11 * m13 + r12 * m23);
+		float r23 = - (r20 * m03 + r21 * m13 + r22 * m23);
+
+		return Matrix4(
+			r00, r01, r02, r03,
+			r10, r11, r12, r13,
+			r20, r21, r22, r23,
+			  0,   0,   0,   1);
+	}
+
+	void Matrix4::setTRS(const Vector3& translation, const Quaternion& rotation, const Vector3& scale)
+	{
+		Matrix3 rot3x3;
+		rotation.toRotationMatrix(rot3x3);
+
+		m[0][0] = scale.x * rot3x3[0][0]; m[0][1] = scale.y * rot3x3[0][1]; m[0][2] = scale.z * rot3x3[0][2]; m[0][3] = translation.x;
+		m[1][0] = scale.x * rot3x3[1][0]; m[1][1] = scale.y * rot3x3[1][1]; m[1][2] = scale.z * rot3x3[1][2]; m[1][3] = translation.y;
+		m[2][0] = scale.x * rot3x3[2][0]; m[2][1] = scale.y * rot3x3[2][1]; m[2][2] = scale.z * rot3x3[2][2]; m[2][3] = translation.z;
+
+		// No projection term
+		m[3][0] = 0; m[3][1] = 0; m[3][2] = 0; m[3][3] = 1;
+	}
+
+	void Matrix4::setInverseTRS(const Vector3& translation, const Quaternion& rotation, const Vector3& scale)
+	{
+		// Invert the parameters
+		Vector3 invTranslate = -translation;
+		Vector3 invScale(1 / scale.x, 1 / scale.y, 1 / scale.z);
+		Quaternion invRot = rotation.inverse();
+
+		// Because we're inverting, order is translation, rotation, scale
+		// So make translation relative to scale & rotation
+		invTranslate = invRot.rotate(invTranslate);
+		invTranslate *= invScale;
+
+		// Next, make a 3x3 rotation matrix
+		Matrix3 rot3x3;
+		invRot.toRotationMatrix(rot3x3);
+
+		// Set up final matrix with scale, rotation and translation
+		m[0][0] = invScale.x * rot3x3[0][0]; m[0][1] = invScale.x * rot3x3[0][1]; m[0][2] = invScale.x * rot3x3[0][2]; m[0][3] = invTranslate.x;
+		m[1][0] = invScale.y * rot3x3[1][0]; m[1][1] = invScale.y * rot3x3[1][1]; m[1][2] = invScale.y * rot3x3[1][2]; m[1][3] = invTranslate.y;
+		m[2][0] = invScale.z * rot3x3[2][0]; m[2][1] = invScale.z * rot3x3[2][1]; m[2][2] = invScale.z * rot3x3[2][2]; m[2][3] = invTranslate.z;		
+
+		// No projection term
+		m[3][0] = 0; m[3][1] = 0; m[3][2] = 0; m[3][3] = 1;
+	}
 
 	void Matrix4::decomposition(Vector3& position, Quaternion& rotation, Vector3& scale) const
 	{

+ 14 - 5
Source/MBansheeEditor/Inspectors/LightInspector.cs

@@ -24,6 +24,7 @@ namespace BansheeEditor
         private GUISliderField spotFalloffAngleField = new GUISliderField(1, 180, new LocEdString("Spot falloff angle"));
         private GUIToggleField autoAttenuationField = new GUIToggleField(new LocEdString("Use auto. attenuation"));
         private GUIToggleField castShadowField = new GUIToggleField(new LocEdString("Cast shadow"));
+        private GUISliderField shadowBiasField = new GUISliderField(-1.0f, 1.0f, new LocEdString("Shadow bias"));
 
         private InspectableState modifyState;
 
@@ -38,7 +39,7 @@ namespace BansheeEditor
                 {
                     light.Type = (LightType)x;
 
-                    ToggleTypeSpecificFields((LightType) x, light.UseAutoAttenuation);
+                    ToggleTypeSpecificFields((LightType) x, light.UseAutoAttenuation, light.CastsShadow);
                 };
 
                 colorField.OnChanged += x =>
@@ -69,6 +70,7 @@ namespace BansheeEditor
                 castShadowField.OnChanged += x =>
                 {
                     light.CastsShadow = x;
+                    ToggleTypeSpecificFields(light.Type, light.UseAutoAttenuation, x);
                     MarkAsModified();
                     ConfirmModify();
                 };
@@ -76,11 +78,14 @@ namespace BansheeEditor
                 autoAttenuationField.OnChanged += x =>
                 {
                     light.UseAutoAttenuation = x;
-                    ToggleTypeSpecificFields(light.Type, x);
+                    ToggleTypeSpecificFields(light.Type, x, light.CastsShadow);
                     MarkAsModified();
                     ConfirmModify();
                 };
 
+                shadowBiasField.OnChanged += x => { light.ShadowBias = x; MarkAsModified(); };
+                shadowBiasField.OnFocusLost += ConfirmModify;
+
                 Layout.AddElement(lightTypeField);
                 Layout.AddElement(colorField);
                 Layout.AddElement(intensityField);
@@ -91,7 +96,7 @@ namespace BansheeEditor
                 Layout.AddElement(autoAttenuationField);
                 Layout.AddElement(castShadowField);
 
-                ToggleTypeSpecificFields(light.Type, light.UseAutoAttenuation);
+                ToggleTypeSpecificFields(light.Type, light.UseAutoAttenuation, light.CastsShadow);
             }
         }
 
@@ -104,7 +109,7 @@ namespace BansheeEditor
 
             LightType lightType = light.Type;
             if (lightTypeField.Value != (ulong)lightType || autoAttenuationField.Value != light.UseAutoAttenuation)
-                ToggleTypeSpecificFields(lightType, light.UseAutoAttenuation);
+                ToggleTypeSpecificFields(lightType, light.UseAutoAttenuation, light.CastsShadow);
 
             lightTypeField.Value = (ulong)lightType;
             colorField.Value = light.Color;
@@ -115,6 +120,7 @@ namespace BansheeEditor
             spotFalloffAngleField.Value = light.SpotAngleFalloff.Degrees;
             autoAttenuationField.Value = light.UseAutoAttenuation;
             castShadowField.Value = light.CastsShadow;
+            shadowBiasField.Value = light.ShadowBias;
 
             InspectableState oldState = modifyState;
             if (modifyState.HasFlag(InspectableState.Modified))
@@ -128,7 +134,8 @@ namespace BansheeEditor
         /// </summary>
         /// <param name="type">Light type to show GUI elements for.</param>
         /// <param name="physBasedAttenuation">Determines is physically based attenuation enabled.</param>
-        private void ToggleTypeSpecificFields(LightType type, bool physBasedAttenuation)
+        /// <param name="castsShadows">Determines if shadow specific options should be shown.</param>
+        private void ToggleTypeSpecificFields(LightType type, bool physBasedAttenuation, bool castsShadows)
         {
             if (type == LightType.Directional)
             {
@@ -151,6 +158,8 @@ namespace BansheeEditor
                 spotFalloffAngleField.Active = true;
                 autoAttenuationField.Active = true;
             }
+
+            shadowBiasField.Active = castsShadows;
         }
 
         /// <summary>

+ 10 - 7
Source/RenderBeast/BsShadowRendering.cpp

@@ -1401,7 +1401,8 @@ namespace bs { namespace ct
 		mapInfo.depthBias = getDepthBias(*light, light->getBounds().getRadius(), mapInfo.depthRange, options.mapSize);
 		mapInfo.subjectBounds = light->getBounds();
 
-		Matrix4 proj = Matrix4::projectionPerspective(Degree(90.0f), 1.0f, 0.05f, light->getAttenuationRadius());
+		// Note: Projecting on positive Z axis, because cubemaps use a left-handed coordinate system
+		Matrix4 proj = Matrix4::projectionPerspective(Degree(90.0f), 1.0f, 0.05f, light->getAttenuationRadius(), true);
 		ConvexVolume localFrustum(proj);
 
 		RenderAPI& rapi = RenderAPI::instance();
@@ -1427,8 +1428,6 @@ namespace bs { namespace ct
 		gShadowParamsDef.gMatViewProj.set(shadowParamsBuffer, Matrix4::IDENTITY);
 		gShadowParamsDef.gNDCZToDeviceZ.set(shadowParamsBuffer, RendererView::getNDCZToDeviceZ());
 
-		Matrix4 viewOffsetMat = Matrix4::translation(-light->getTransform().getPosition());
-
 		ConvexVolume frustums[6];
 		Vector<Plane> boundingPlanes;
 		for (UINT32 i = 0; i < 6; i++)
@@ -1462,9 +1461,12 @@ namespace bs { namespace ct
 			}
 
 			Vector3 right = Vector3::cross(up, forward);
-			Matrix3 viewRotationMat = Matrix3(right, up, -forward);
+			Matrix3 viewRotationMat = Matrix3(right, up, forward);
+
+			Vector3 lightPos = light->getTransform().getPosition();
+			Matrix4 viewOffsetMat = Matrix4::translation(-lightPos);
 
-			Matrix4 view = Matrix4(viewRotationMat) * viewOffsetMat;
+			Matrix4 view = Matrix4(viewRotationMat.transpose()) * viewOffsetMat;
 			mapInfo.shadowVPTransforms[i] = proj * view;
 
 			Matrix4 shadowViewProj = adjustedProj * view;
@@ -1472,7 +1474,8 @@ namespace bs { namespace ct
 
 			// Calculate world frustum for culling
 			const Vector<Plane>& frustumPlanes = localFrustum.getPlanes();
-			Matrix4 worldMatrix = view.transpose();
+
+			Matrix4 worldMatrix = Matrix4::translation(lightPos) * Matrix4(viewRotationMat);
 
 			Vector<Plane> worldPlanes(frustumPlanes.size());
 			UINT32 j = 0;
@@ -1808,7 +1811,7 @@ namespace bs { namespace ct
 
 	float ShadowRendering::getDepthBias(const Light& light, float radius, float depthRange, UINT32 mapSize)
 	{
-		const static float RADIAL_LIGHT_BIAS = 0.00005f;
+		const static float RADIAL_LIGHT_BIAS = 0.0005f;
 		const static float SPOT_DEPTH_BIAS = 0.01f;
 		const static float DIR_DEPTH_BIAS = 0.5f;
 		const static float DEFAULT_RESOLUTION = 512.0f;