소스 검색

IS support for spot lights

Panagiotis Christopoulos Charitos 13 년 전
부모
커밋
5646796c9d
4개의 변경된 파일199개의 추가작업 그리고 126개의 파일을 삭제
  1. 29 22
      include/anki/renderer/Is.h
  2. 5 0
      include/anki/scene/Light.h
  3. 29 8
      shaders/IsLpGeneric.glsl
  4. 136 96
      src/renderer/Is.cpp

+ 29 - 22
include/anki/renderer/Is.h

@@ -27,6 +27,7 @@ class Is: private RenderingPass
 	friend struct WritePointLightsUbo;
 	friend struct WriteSpotLightsUbo;
 	friend struct WriteTilesUboJob;
+	friend struct UpdateTilesJob;
 
 public:
 	// Config. These values affect the size of the uniform blocks and keep in
@@ -40,7 +41,6 @@ public:
 	static const U MAX_SPOT_LIGHTS = 8;
 
 	Is(Renderer* r);
-
 	~Is();
 
 	void init(const RendererInitializer& initializer);
@@ -59,15 +59,7 @@ public:
 	}
 	/// @}
 
-private:
-	enum LightSubType
-	{
-		LST_POINT,
-		LST_SPOT,
-		LST_SPOT_SHADOW,
-		LST_COUNT
-	};
-
+private
 	/// A screen tile
 	struct Tile
 	{
@@ -76,6 +68,7 @@ private:
 
 		/// Frustum planes
 		Array<Plane, 6> planes;
+		Array<Plane, 6> planesWSpace;
 	};
 
 	static const U COMMON_UNIFORMS_BLOCK_BINDING = 0;
@@ -86,7 +79,11 @@ private:
 	U32 planesUpdateTimestamp = Timestamp::getTimestamp();
 
 	/// @note The [0][0] is the bottom left tile
-	Array<Array<Tile, TILES_X_COUNT>, TILES_Y_COUNT> tiles;
+	union
+	{
+		Array<Array<Tile, TILES_X_COUNT>, TILES_Y_COUNT> tiles;
+		Array<Tile, TILES_X_COUNT * TILES_Y_COUNT> tiles1d;
+	};
 
 	/// A texture of TILES_X_COUNT*TILES_Y_COUNT size and format RG16F. Used to
 	/// to fill the Tile::depth
@@ -120,37 +117,47 @@ private:
 	/// Light shaders
 	ShaderProgramResourcePointer lightPassProg;
 
+	/// Shadow mapping
 	Sm sm;
 
+	/// Called by init
 	void initInternal(const RendererInitializer& initializer);
 
+	/// Do minmax pass and set the planes of the tiles
+	void updateTiles();
+
 	/// Updates all the planes except the near and far plane. Near and far 
 	/// planes will be updated in min max pass when the depth is known
-	void updateAllTilesPlanes();
+	void updateTilePlanes(F32 (*pixels)[TILES_Y_COUNT][TILES_X_COUNT][2],
+		U32 start, U32 finish);
 
-	void updateAllTilesPlanesInternal(const PerspectiveCamera& pcam);
+	/// Update only the 4 planes of the tiles
+	void updateTiles4Planes(U32 start, U32 stop);
 
-	/// Do minmax pass and set the near/far planes of the tiles
-	void updateTiles();
+	/// Update the 4 planes of the tile for a perspective camera
+	void updateTiles4PlanesInternal(const PerspectiveCamera& cam,
+		U32 start, U32 stop)
 
 	/// See if the light is inside the tile
-	static Bool cullLight(const PointLight& light, const Tile& tile, 
-		const Mat4& viewMatrix);
-	static Bool cullLight(const SpotLight& light, const Tile& tile,
-		const Mat4& viewMatrix);
+	static Bool cullLight(const PointLight& light, const Tile& tile);
+	static Bool cullLight(const SpotLight& light, const Tile& tile);
 
+	/// Update the point lights UBO
 	void writeLightUbo(ShaderPointLights& shaderLights, U32 maxShaderLights,
-		PointLight** visibleLights, U32 visibleLightsCount, U start, U end);
+		PointLight* visibleLights[], U32 visibleLightsCount, U start, U end);
+	/// Update the spot lights UBO
 	void writeLightUbo(ShaderSpotLights& shaderLights, U32 maxShaderLights,
-		SpotLight** visibleLights, U32 visibleLightsCount, U start, U end);
+		SpotLight* visibleLights[], U32 visibleLightsCount, U start, U end);
 
+	/// Write the tiles UBO
 	void writeTilesUbo(
 		PointLight* visiblePointLights[], U32 visiblePointLightsCount,
 		SpotLight* visibleSpotLights[], U32 visibleSpotLightsCount,
 		ShaderTiles& shaderTiles, U32 maxLightsPerTile,
 		U32 start, U32 end);
 
-	void pointLightsPass();
+	// Do the actual pass
+	void lightPass();
 };
 
 } // end namespace anki

+ 5 - 0
include/anki/scene/Light.h

@@ -220,6 +220,11 @@ public:
 	{
 		setFar(f);
 	}
+
+	const PerspectiveFrustum& getFrustum() const
+	{
+		return frustum;
+	}
 	/// @}
 
 	/// @name SceneNode virtuals

+ 29 - 8
shaders/IsLpGeneric.glsl

@@ -70,7 +70,7 @@ layout(std140, row_major, binding = 2) uniform spotLightsBlock
 
 struct Tile
 {
-	uint lightsCount;
+	uvec4 lightsCount;
 	uvec4 lightIndices[MAX_LIGHTS_PER_TILE / 4];
 };
 
@@ -81,9 +81,9 @@ layout(std140, row_major, binding = 3) uniform tilesBlock
 
 uniform usampler2D msFai0;
 uniform sampler2D msDepthFai;
-uniform sampler2D lightTex;
-uniform sampler2DShadow shadowMap;
-uniform sampler2D minmax;
+
+uniform sampler2D lightTextures[MAX_SPOT_LIGHTS];
+uniform sampler2DShadow shadowMap[MAX_SPOT_LIGHTS];
 /// @}
 
 /// @name Varyings
@@ -179,11 +179,12 @@ void main()
 	vec4 diffuseAndSpec = unpackUnorm4x8(msAll[0]);
 	vec2 specularAll = unpackSpecular(diffuseAndSpec.a);
 
-	// Lighting
-	uint lightsCount = tiles[vInstanceId].lightsCount;
-
+	// Ambient color
 	fColor = diffuseAndSpec.rgb * sceneAmbientColor.rgb;
-	for(uint i = 0; i < lightsCount; ++i)
+
+	// Point lights
+	uint pointLightsCount = tiles[vInstanceId].lightsCount[0];
+	for(uint i = 0; i < pointLightsCount; ++i)
 	{
 		uint lightId = tiles[vInstanceId].lightIndices[i / 4][i % 4];
 
@@ -191,6 +192,26 @@ void main()
 			specularAll, plights[lightId]);
 	}
 
+	// Spot lights
+	uint spotLightsCount = tiles[vInstanceId].lightsCount[1];
+
+	for(uint i = pointLightsCount; i < pointLightsCount + spotLightsCount; ++i)
+	{
+		uint lightId = tiles[vInstanceId].lightIndices[i / 4][i % 4];
+
+		vec4 texCoords2 = slights[lightId].texProjectionMat 
+			* vec4(fragPosVspace, 1.0);
+		vec3 texCoords3 = texCoords2.xyz / texCoords2.w;
+		
+		vec2 pureColor = doPhong(fragPosVspace, normal, diffuseAndSpec.rgb, 
+			specularAll, plights[lightId].light);
+
+		vec3 lightTexColor = 
+			textureProj(lightTextures[lightId], texCoords2.xyz).rgb;
+
+		fColor += pureColor * lightTexColor;
+	}
+
 #if 0
 	float depth = texture(msDepthFai, vTexCoords).r;
 	vec2 mm = texture(minmax, vTexCoords).rg;

+ 136 - 96
src/renderer/Is.cpp

@@ -11,7 +11,7 @@ namespace anki {
 
 //==============================================================================
 
-// Shader struct and block representations
+// Shader structs and block representations
 
 struct ShaderLight
 {
@@ -40,7 +40,7 @@ struct ShaderSpotLights
 
 struct ShaderTile
 {
-	U32 lightsCount[2];
+	U32 lightsCount[2]; ///< 0: Point lights number, 1: Spot lights number
 	U32 padding[2];
 	// When change this change the writeTilesUbo as well
 	Array<U32, Is::MAX_LIGHTS_PER_TILE> lightIndices; 
@@ -122,6 +122,22 @@ struct WriteTilesUboJob: ThreadJob
 	}
 };
 
+/// Job that updates the tile planes
+struct UpdateTilesJob: ThreadJob
+{
+	F32 (*pixels)[TILES_Y_COUNT][TILES_X_COUNT][2];
+	Is* is;
+
+	void operator()(U threadId, U threadsCount)
+	{
+		U64 start, end;
+		choseStartEnd(threadId, threadsCount, 
+			Is::TILES_X_COUNT * Is::TILES_Y_COUNT, start, end);
+
+		is->updateTilePlanes(pixels, start, end);
+	}
+};
+
 //==============================================================================
 Is::Is(Renderer* r_)
 	: RenderingPass(r_), sm(r_)
@@ -253,13 +269,11 @@ void Is::initInternal(const RendererInitializer& initializer)
 }
 
 //==============================================================================
-Bool Is::cullLight(const PointLight& plight, const Tile& tile, 
-	const Mat4& viewMatrix)
+Bool Is::cullLight(const PointLight& plight, const Tile& tile)
 {
-	Sphere sphere = plight.getSphere();
-	sphere.transform(Transform(viewMatrix));
+	const Sphere& sphere = plight.getSphere();
 
-	for(const Plane& plane : tile.planes)
+	for(const Plane& plane : tile.planesWSpace)
 	{
 		if(sphere.testPlane(plane) < 0.0)
 		{
@@ -271,71 +285,88 @@ Bool Is::cullLight(const PointLight& plight, const Tile& tile,
 }
 
 //==============================================================================
-Bool Is::cullLight(const SpotLight& light, const Tile& tile, 
-	const Mat4& viewMatrix)
+Bool Is::cullLight(const SpotLight& light, const Tile& tile)
 {
-	/// XXX
-	return false;
+	const PerspectiveFrustum& fr = light.getFrustum();
+
+	for(const Plane& plane : tile.planesWSpace)
+	{
+		if(fr.testPlane(plane) < 0.0)
+		{
+			return false;
+		}
+	}
+
+	return true;
 }
 
 //==============================================================================
-void Is::updateAllTilesPlanesInternal(const PerspectiveCamera& cam)
+void Is::updateTiles()
 {
-	// The algorithm is almost the same as the recalculation of planes for
-	// PerspectiveFrustum class
+	// Do the min/max pass
+	//
+	minMaxTilerFbo.bind();
+	minMaxPassSprog->bind();
+	GlStateSingleton::get().setViewport(0, 0, TILES_X_COUNT, TILES_Y_COUNT);
 
-	// Algorithm works for even number of tiles per dimension
-	ANKI_ASSERT(TILES_X_COUNT % 2 == 0 && TILES_Y_COUNT % 2 == 0);
+	minMaxPassSprog->findUniformVariable("depthMap").set(
+		r->getMs().getDepthFai());
 
-	F32 fx = cam.getFovX();
-	F32 fy = cam.getFovY();
-	F32 n = cam.getNear();
+	r->drawQuad();
 
-	F32 l = 2.0 * n * tan(fx / 2.0);
-	F32 l6 = l / TILES_X_COUNT;
-	F32 o = 2.0 * n * tan(fy / 2.0);
-	F32 o6 = o / TILES_Y_COUNT;
+	F32 pixels[TILES_Y_COUNT][TILES_X_COUNT][2];
+	minMaxFai.readPixels(pixels);
 
-	for(U j = 0; j < TILES_Y_COUNT; j++)
-	{
-		for(U i = 0; i < TILES_X_COUNT; i++)
-		{
-			Array<Plane, Frustum::FP_COUNT>& planes = tiles[j][i].planes;
-			Vec3 a, b;
+	// Update the rest of the tile stuff in parallel
+	// 
 
-			// left
-			a = Vec3((I(i) - I(TILES_X_COUNT) / 2) * l6, 0.0, -n);
-			b = a.cross(Vec3(0.0, 1.0, 0.0));
-			b.normalize();
+	ThreadPool& threadPool = ThreadPoolSingleton::get();
+	UpdateTilesJob jobs[ThreadPool::MAX_THREADS]
+	
+	for(U i = 0; i < threadPool.getThreadsCount(); i++)
+	{
+		job[i].pixels = &pixels;
+		job[i].is = this;
 
-			planes[Frustum::FP_LEFT] = Plane(b, 0.0);
+		threadPool.assignNewJob(i, &jobs[i]);
+	}
 
-			// right
-			a = Vec3((I(i) - I(TILES_X_COUNT) / 2 + 1) * l6, 0.0, -n);
-			b = Vec3(0.0, 1.0, 0.0).cross(a);
-			b.normalize();
+	threadPool.waitForAllJobsToFinish();
+}
 
-			planes[Frustum::FP_RIGHT] = Plane(b, 0.0);
+//==============================================================================
+void Is::updateTilePlanes(F32 (*pixels)[TILES_Y_COUNT][TILES_X_COUNT][2],
+	U32 start, U32 finish)
+{
+	// Update only the 4 planes
+	updateTiles4Planes(start, stop);
 
-			// bottom
-			a = Vec3(0.0, (I(j) - I(TILES_Y_COUNT) / 2) * o6, -n);
-			b = Vec3(1.0, 0.0, 0.0).cross(a);
-			b.normalize();
+	// - Calc the rest of the 2 planes and 
+	// - transform the planes
+	for(U32 k = start; k < stop; k++)
+	{
+		Tile& tile = tiles[j][i];
 
-			planes[Frustum::FP_BOTTOM] = Plane(b, 0.0);
+		/// Calculate as you do in the vertex position inside the shaders
+		F32 minZ = 
+			r->getPlanes().y() / (r->getPlanes().x() + (*pixels)[j][i][0]);
+		F32 maxZ = 
+			-r->getPlanes().y() / (r->getPlanes().x() + (*pixels)[j][i][1]);
 
-			// bottom
-			a = Vec3(0.0, (I(j) - I(TILES_Y_COUNT) / 2 + 1) * o6, -n);
-			b = a.cross(Vec3(1.0, 0.0, 0.0));
-			b.normalize();
+		tile.planes[Frustum::FP_NEAR] = Plane(Vec3(0.0, 0.0, -1.0), minZ);
+		tile.planes[Frustum::FP_FAR] = Plane(Vec3(0.0, 0.0, 1.0), maxZ);
 
-			planes[Frustum::FP_TOP] = Plane(b, 0.0);
+		// Now transform
+		for(U k = 0; k < 6; k++)
+		{
+			tile.planesWSpace[k] = tile.planes[k].getTransformed(
+				Transform(cam.getViewMatrix()));
 		}
 	}
 }
 
 //==============================================================================
-void Is::updateAllTilesPlanes()
+void Is::updateTiles4Planes(U32 start, U32 stop)
 {
 	Camera& cam = r->getScene().getActiveCamera();
 	U32 camTimestamp = cam.getFrustumable()->getFrustumableTimestamp();
@@ -348,8 +379,8 @@ void Is::updateAllTilesPlanes()
 	switch(cam.getCameraType())
 	{
 	case Camera::CT_PERSPECTIVE:
-		updateAllTilesPlanesInternal(
-			static_cast<const PerspectiveCamera&>(cam));
+		updateTiles4PlanesInternal(
+			static_cast<const PerspectiveCamera&>(cam), start, stop);
 		break;
 	default:
 		ANKI_ASSERT(0 && "Unimplemented");
@@ -360,52 +391,65 @@ void Is::updateAllTilesPlanes()
 }
 
 //==============================================================================
-void Is::updateTiles()
+void Is::updateTiles4PlanesInternal(const PerspectiveCamera& cam,
+	U32 start, U32 stop)
 {
-	// Do the min/max pass
-	//
-	const Camera& cam = r->getScene().getActiveCamera();
+	// The algorithm is almost the same as the recalculation of planes for
+	// PerspectiveFrustum class
 
-	minMaxTilerFbo.bind();
-	minMaxPassSprog->bind();
-	GlStateSingleton::get().setViewport(0, 0, TILES_X_COUNT, TILES_Y_COUNT);
+	// Algorithm works for even number of tiles per dimension
+	ANKI_ASSERT(TILES_X_COUNT % 2 == 0 && TILES_Y_COUNT % 2 == 0);
 
-	minMaxPassSprog->findUniformVariable("depthMap").set(
-		r->getMs().getDepthFai());
+	F32 fx = cam.getFovX();
+	F32 fy = cam.getFovY();
+	F32 n = cam.getNear();
 
-	r->drawQuad();
+	F32 l = 2.0 * n * tan(fx / 2.0);
+	F32 l6 = l / TILES_X_COUNT;
+	F32 o = 2.0 * n * tan(fy / 2.0);
+	F32 o6 = o / TILES_Y_COUNT;
 
-	// Do something else instead of waiting for the draw call to finish.
-	// Update the tile planes
-	//
-	updateAllTilesPlanes();
+	for(U32 k = start; k < stop; k++)
+	{
+		U i = k % TILES_X_COUNT;
+		U j = k / TILES_X_COUNT;
 
-	// Update the near and far planes of tiles
-	//
-	F32 pixels[TILES_Y_COUNT][TILES_X_COUNT][2];
-	minMaxFai.readPixels(pixels);
+		Array<Plane, Frustum::FP_COUNT>& planes = tiles[j][i].planes;
+		Vec3 a, b;
 
-	for(U j = 0; j < TILES_Y_COUNT; j++)
-	{
-		for(U i = 0; i < TILES_X_COUNT; i++)
-		{
-			Tile& tile = tiles[j][i];
+		// left
+		a = Vec3((I(i) - I(TILES_X_COUNT) / 2) * l6, 0.0, -n);
+		b = a.cross(Vec3(0.0, 1.0, 0.0));
+		b.normalize();
 
-			/// Calculate as you do in the vertex position inside the shaders
-			F32 minZ =
-				r->getPlanes().y() / (r->getPlanes().x() + pixels[j][i][0]);
-			F32 maxZ =
-				-r->getPlanes().y() / (r->getPlanes().x() + pixels[j][i][1]);
+		planes[Frustum::FP_LEFT] = Plane(b, 0.0);
 
-			tile.planes[Frustum::FP_NEAR] = Plane(Vec3(0.0, 0.0, -1.0), minZ);
-			tile.planes[Frustum::FP_FAR] = Plane(Vec3(0.0, 0.0, 1.0), maxZ);
-		}
+		// right
+		a = Vec3((I(i) - I(TILES_X_COUNT) / 2 + 1) * l6, 0.0, -n);
+		b = Vec3(0.0, 1.0, 0.0).cross(a);
+		b.normalize();
+
+		planes[Frustum::FP_RIGHT] = Plane(b, 0.0);
+
+		// bottom
+		a = Vec3(0.0, (I(j) - I(TILES_Y_COUNT) / 2) * o6, -n);
+		b = Vec3(1.0, 0.0, 0.0).cross(a);
+		b.normalize();
+
+		planes[Frustum::FP_BOTTOM] = Plane(b, 0.0);
+
+		// bottom
+		a = Vec3(0.0, (I(j) - I(TILES_Y_COUNT) / 2 + 1) * o6, -n);
+		b = a.cross(Vec3(1.0, 0.0, 0.0));
+		b.normalize();
+
+		planes[Frustum::FP_TOP] = Plane(b, 0.0);
 	}
 }
 
 //==============================================================================
 void Is::writeLightUbo(ShaderPointLights& shaderLights, U32 maxShaderLights,
-	PointLight** visibleLights, U32 visibleLightsCount, U start, U end)
+	PointLight* visibleLights[], U32 visibleLightsCount, U start, U end)
 {
 	const Camera& cam = r->getScene().getActiveCamera();
 
@@ -426,7 +470,7 @@ void Is::writeLightUbo(ShaderPointLights& shaderLights, U32 maxShaderLights,
 
 //==============================================================================
 void Is::writeLightUbo(ShaderSpotLights& shaderLights, U32 maxShaderLights,
-	SpotLight** visibleLights, U32 visibleLightsCount, U start, U end)
+	SpotLight* visibleLights[], U32 visibleLightsCount, U start, U end)
 {
 	const Camera& cam = r->getScene().getActiveCamera();
 
@@ -458,15 +502,12 @@ void Is::writeTilesUbo(
 	ShaderTiles& shaderTiles, U32 maxLightsPerTile,
 	U32 start, U32 end)
 {
-	Tile* tiles_ = &tiles[0][0];
 	const Camera& cam = r->getScene().getActiveCamera();
 	ANKI_ASSERT(maxLightsPerTile <= MAX_LIGHTS_PER_TILE);
 
 	for(U32 i = start; i < end; i++)
 	{
-		ANKI_ASSERT(i < TILES_X_COUNT * TILES_Y_COUNT);
-
-		Tile& tile = tiles_[i];
+		Tile& tile = tiles1d[i];
 		Array<U32, MAX_LIGHTS_PER_TILE> lightIndices;
 
 		// Point lights
@@ -477,7 +518,7 @@ void Is::writeTilesUbo(
 		{
 			const PointLight& light = *visiblePointLights[j];
 
-			if(cullLight(light, tile, cam.getViewMatrix()))
+			if(cullLight(light, tile))
 			{
 				// Use % to avoid overflows
 				lightIndices[pointLightsInTileCount % maxLightsPerTile] = j;
@@ -495,7 +536,7 @@ void Is::writeTilesUbo(
 		{
 			const SpotLight& light = *visibleSpotLights[j];
 
-			if(cullLight(light, tile, cam.getViewMatrix()))
+			if(cullLight(light, tile))
 			{
 				U id = (pointLightsInTileCount + spotLightsInTileCount) 
 					% maxLightsPerTile;
@@ -526,7 +567,7 @@ void Is::writeTilesUbo(
 }
 
 //==============================================================================
-void Is::pointLightsPass()
+void Is::lightPass()
 {
 	ThreadPool& threadPool = ThreadPoolSingleton::get();
 	Camera& cam = r->getScene().getActiveCamera();
@@ -619,7 +660,7 @@ void Is::pointLightsPass()
 	spotLightsUbo.unmap();
 
 	//
-	// Update the tiles
+	// Update the tiles UBO
 	//
 
 	ShaderTiles* stiles = (ShaderTiles*)tilesUbo.map(
@@ -688,11 +729,10 @@ void Is::run()
 	// Update tiles
 	updateTiles();
 
-	// Do the rest
+	// Do the light pass
 	fbo.bind();
 	GlStateSingleton::get().setViewport(0, 0, r->getWidth(), r->getHeight());
-
-	pointLightsPass();
+	lightPass();
 }
 
 } // end namespace anki