Sfoglia il codice sorgente

Add some bits for textured skyboxes

Panagiotis Christopoulos Charitos 3 anni fa
parent
commit
6ba3828839

+ 10 - 0
AnKi/Importer/GltfImporter.cpp

@@ -490,6 +490,16 @@ Error GltfImporter::visitNode(const cgltf_node& node, const Transform& parentTrf
 			ANKI_CHECK(getNodeTransform(node, localTrf));
 			ANKI_CHECK(writeTransform(parentTrf.combineTransformations(localTrf)));
 		}
+		else if((it = extras.find("skybox_image")) != extras.getEnd())
+		{
+			ANKI_CHECK(m_sceneFile.writeText("\nnode = scene:newSkyboxNode(\"%s\")\n", getNodeName(node).cstr()));
+			ANKI_CHECK(m_sceneFile.writeText("comp = node:getSceneNodeBase():getSkyboxComponent()\n"));
+			ANKI_CHECK(m_sceneFile.writeText("comp:setImage(\"%s\")\n", it->cstr()));
+
+			Transform localTrf;
+			ANKI_CHECK(getNodeTransform(node, localTrf));
+			ANKI_CHECK(writeTransform(parentTrf.combineTransformations(localTrf)));
+		}
 		else if((it = extras.find("collision")) != extras.getEnd() && *it == "true")
 		{
 			ANKI_CHECK(

+ 158 - 11
AnKi/Importer/ImageImporter.cpp

@@ -204,6 +204,10 @@ static ANKI_USE_RESULT Error checkConfig(const ImageImporterConfig& config)
 	// Mip size
 	ANKI_CFG_ASSERT(config.m_minMipmapDimension >= 4, "Mimpap min dimension can be less than 4");
 
+	// Color conversions
+	ANKI_CFG_ASSERT(!(config.m_linearToSRgb && config.m_sRgbToLinear),
+					"Can't have a conversion to sRGB and to linear at the same time");
+
 #undef ANKI_CFG_ASSERT
 	return Error::NONE;
 }
@@ -264,6 +268,71 @@ static ANKI_USE_RESULT Error checkInputImages(const ImageImporterConfig& config,
 	return Error::NONE;
 }
 
+static Vec3 linearToSRgb(Vec3 p)
+{
+	Vec3 cutoff;
+	cutoff.x() = (p.x() < 0.0031308f) ? 1.0f : 0.0f;
+	cutoff.y() = (p.y() < 0.0031308f) ? 1.0f : 0.0f;
+	cutoff.z() = (p.z() < 0.0031308f) ? 1.0f : 0.0f;
+
+	const Vec3 higher = 1.055f * p.pow(1.0f / 2.4f) - 0.055f;
+	const Vec3 lower = p * 12.92f;
+	p = higher.lerp(lower, cutoff);
+
+	return p;
+}
+
+static Vec3 sRgbToLinear(Vec3 p)
+{
+	Vec3 cutoff;
+	cutoff.x() = (p.x() < 0.04045f) ? 1.0f : 0.0f;
+	cutoff.y() = (p.y() < 0.04045f) ? 1.0f : 0.0f;
+	cutoff.z() = (p.z() < 0.04045f) ? 1.0f : 0.0f;
+
+	const Vec3 higher = ((p + 0.055f) / 1.055f).pow(2.4f);
+	const Vec3 lower = p / 12.92f;
+	return higher.lerp(lower, cutoff);
+}
+
+template<typename TVec, typename TFunc>
+static void linearToSRgbBatch(WeakArray<TVec> pixels, TFunc func)
+{
+	using S = typename TVec::Scalar;
+
+	for(TVec& pixel : pixels)
+	{
+		Vec3 p;
+		if(std::is_same<S, U8>::value)
+		{
+			p.x() = F32(pixel.x()) / 255.0f;
+			p.y() = F32(pixel.y()) / 255.0f;
+			p.z() = F32(pixel.z()) / 255.0f;
+		}
+		else
+		{
+			ANKI_ASSERT((std::is_same<S, F32>::value));
+			p.x() = F32(pixel.x());
+			p.y() = F32(pixel.y());
+			p.z() = F32(pixel.z());
+		}
+
+		p = func(p);
+
+		if(std::is_same<S, U8>::value)
+		{
+			pixel.x() = S(p.x() / 255.0f);
+			pixel.y() = S(p.y() / 255.0f);
+			pixel.z() = S(p.z() / 255.0f);
+		}
+		else
+		{
+			pixel.x() = p.x();
+			pixel.y() = p.y();
+			pixel.z() = p.z();
+		}
+	}
+}
+
 static ANKI_USE_RESULT Error loadFirstMipmap(const ImageImporterConfig& config, ImageImporterContext& ctx)
 {
 	GenericMemoryPoolAllocator<U8> alloc = ctx.getAllocator();
@@ -314,6 +383,72 @@ static ANKI_USE_RESULT Error loadFirstMipmap(const ImageImporterConfig& config,
 
 		const PtrSize dataSize = PtrSize(ctx.m_width) * ctx.m_height * ctx.m_pixelSize;
 
+		// To conversions in place
+		if(config.m_linearToSRgb)
+		{
+			ANKI_IMPORTER_LOGV("Will convert linear to sRGB");
+
+			if(ctx.m_channelCount == 3)
+			{
+				if(!ctx.m_hdr)
+				{
+					linearToSRgbBatch(WeakArray<U8Vec3>(static_cast<U8Vec3*>(data), ctx.m_width * ctx.m_height),
+									  linearToSRgb);
+				}
+				else
+				{
+					linearToSRgbBatch(WeakArray<Vec3>(static_cast<Vec3*>(data), ctx.m_width * ctx.m_height),
+									  linearToSRgb);
+				}
+			}
+			else
+			{
+				ANKI_ASSERT(ctx.m_channelCount == 4);
+				if(!ctx.m_hdr)
+				{
+					linearToSRgbBatch(WeakArray<U8Vec4>(static_cast<U8Vec4*>(data), ctx.m_width * ctx.m_height),
+									  linearToSRgb);
+				}
+				else
+				{
+					linearToSRgbBatch(WeakArray<Vec4>(static_cast<Vec4*>(data), ctx.m_width * ctx.m_height),
+									  linearToSRgb);
+				}
+			}
+		}
+		else if(config.m_sRgbToLinear)
+		{
+			ANKI_IMPORTER_LOGV("Will convert sRGB to linear");
+
+			if(ctx.m_channelCount == 3)
+			{
+				if(!ctx.m_hdr)
+				{
+					linearToSRgbBatch(WeakArray<U8Vec3>(static_cast<U8Vec3*>(data), ctx.m_width * ctx.m_height),
+									  sRgbToLinear);
+				}
+				else
+				{
+					linearToSRgbBatch(WeakArray<Vec3>(static_cast<Vec3*>(data), ctx.m_width * ctx.m_height),
+									  sRgbToLinear);
+				}
+			}
+			else
+			{
+				ANKI_ASSERT(ctx.m_channelCount == 4);
+				if(!ctx.m_hdr)
+				{
+					linearToSRgbBatch(WeakArray<U8Vec4>(static_cast<U8Vec4*>(data), ctx.m_width * ctx.m_height),
+									  sRgbToLinear);
+				}
+				else
+				{
+					linearToSRgbBatch(WeakArray<Vec4>(static_cast<Vec4*>(data), ctx.m_width * ctx.m_height),
+									  sRgbToLinear);
+				}
+			}
+		}
+
 		if(ctx.m_depth > 1)
 		{
 			memcpy(mip0.m_surfacesOrVolume.getBegin() + i * dataSize, data, dataSize);
@@ -492,7 +627,7 @@ static ANKI_USE_RESULT Error compressS3tc(GenericMemoryPoolAllocator<U8> alloc,
 
 static ANKI_USE_RESULT Error compressAstc(GenericMemoryPoolAllocator<U8> alloc, CString tempDirectory,
 										  CString astcencPath, ConstWeakArray<U8, PtrSize> inPixels, U32 inWidth,
-										  U32 inHeight, U32 inChannelCount, UVec2 blockSize,
+										  U32 inHeight, U32 inChannelCount, UVec2 blockSize, Bool hdr,
 										  WeakArray<U8, PtrSize> outPixels)
 {
 	const PtrSize blockBytes = 16;
@@ -502,15 +637,28 @@ static ANKI_USE_RESULT Error compressAstc(GenericMemoryPoolAllocator<U8> alloc,
 	ANKI_ASSERT(outPixels.getSizeInBytes() == blockBytes * (inWidth / blockSize.x()) * (inHeight / blockSize.y()));
 
 	// Create a BMP image to feed to the astcebc
-	StringAuto pngFilename(alloc);
-	pngFilename.sprintf("%s/AnKiImageImporter_%u.png", tempDirectory.cstr(), U32(std::rand()));
-	ANKI_IMPORTER_LOGV("Will store: %s", pngFilename.cstr());
-	if(!stbi_write_png(pngFilename.cstr(), inWidth, inHeight, inChannelCount, inPixels.getBegin(), 0))
+	StringAuto tmpFilename(alloc);
+	tmpFilename.sprintf("%s/AnKiImageImporter_%u.%s", tempDirectory.cstr(), U32(std::rand()), (hdr) ? "exr" : "png");
+	ANKI_IMPORTER_LOGV("Will store: %s", tmpFilename.cstr());
+	Bool saveTmpImageOk = false;
+	if(!hdr)
+	{
+		const I ok = stbi_write_png(tmpFilename.cstr(), inWidth, inHeight, inChannelCount, inPixels.getBegin(), 0);
+		saveTmpImageOk = !!ok;
+	}
+	else
 	{
-		ANKI_IMPORTER_LOGE("STB failed to create: %s", pngFilename.cstr());
+		const I ret = SaveEXR(reinterpret_cast<const F32*>(inPixels.getBegin()), inWidth, inHeight, inChannelCount, 0,
+							  tmpFilename.cstr(), nullptr);
+		saveTmpImageOk = ret >= 0;
+	}
+
+	if(!saveTmpImageOk)
+	{
+		ANKI_IMPORTER_LOGE("Failed to create: %s", tmpFilename.cstr());
 		return Error::FUNCTION_FAILED;
 	}
-	CleanupFile pngCleanup(alloc, pngFilename);
+	CleanupFile pngCleanup(alloc, tmpFilename);
 
 	// Invoke the compressor process
 	StringAuto astcFilename(alloc);
@@ -520,8 +668,8 @@ static ANKI_USE_RESULT Error compressAstc(GenericMemoryPoolAllocator<U8> alloc,
 	Process proc;
 	Array<CString, 5> args;
 	U32 argCount = 0;
-	args[argCount++] = "-cl";
-	args[argCount++] = pngFilename;
+	args[argCount++] = (!hdr) ? "-cl" : "-ch";
+	args[argCount++] = tmpFilename;
 	args[argCount++] = astcFilename;
 	args[argCount++] = blockStr;
 	args[argCount++] = "-fast";
@@ -839,7 +987,6 @@ static ANKI_USE_RESULT Error importImageInternal(const ImageImporterConfig& conf
 
 	if(!!(config.m_compressions & ImageBinaryDataCompression::ASTC))
 	{
-		ANKI_ASSERT(!ctx.m_hdr && "TODO");
 		ANKI_IMPORTER_LOGV("Will compress in ASTC");
 
 		for(U32 mip = 0; mip < mipCount; ++mip)
@@ -861,7 +1008,7 @@ static ANKI_USE_RESULT Error importImageInternal(const ImageImporterConfig& conf
 
 					ANKI_CHECK(compressAstc(alloc, config.m_tempDirectory, config.m_astcencPath,
 											ConstWeakArray<U8, PtrSize>(surface.m_pixels), width, height,
-											ctx.m_channelCount, config.m_astcBlockSize,
+											ctx.m_channelCount, config.m_astcBlockSize, ctx.m_hdr,
 											WeakArray<U8, PtrSize>(surface.m_astcPixels)));
 				}
 			}

+ 2 - 0
AnKi/Importer/ImageImporter.h

@@ -30,6 +30,8 @@ public:
 	CString m_compressonatorPath; ///< Optional.
 	CString m_astcencPath; ///< Optional.
 	UVec2 m_astcBlockSize = UVec2(8u);
+	Bool m_sRgbToLinear = false;
+	Bool m_linearToSRgb = false;
 };
 
 /// Converts images to AnKi's specific format.

+ 29 - 1
AnKi/Math/Vec.h

@@ -3114,10 +3114,21 @@ public:
 	}
 #endif
 
+	/// Return lerp(this, v1, t)
+	TVec lerp(const TVec& v1, const TVec& t) const
+	{
+		TVec out;
+		for(U i = 0; i < N; ++i)
+		{
+			out[i] = m_arr[i] * (T(1) - t.m_arr[i]) + v1.m_arr[i] * t.m_arr[i];
+		}
+		return out;
+	}
+
 	/// Return lerp(this, v1, t)
 	TVec lerp(const TVec& v1, T t) const
 	{
-		return ((*this) * (1.0 - t)) + (v1 * t);
+		return ((*this) * (T(1) - t)) + (v1 * t);
 	}
 
 	ANKI_ENABLE_METHOD(!HAS_VEC4_SIMD)
@@ -3229,6 +3240,23 @@ public:
 		return out;
 	}
 
+	/// Power
+	TVec pow(const TVec& b) const
+	{
+		TVec out;
+		for(U i = 0; i < N; ++i)
+		{
+			out[i] = anki::pow(m_arr[i], b.m_arr[i]);
+		}
+		return out;
+	}
+
+	/// Power
+	TVec pow(T b) const
+	{
+		return pow(TVec(b));
+	}
+
 	/// Serialize the structure.
 	void serialize(void* data, PtrSize& size) const
 	{

+ 38 - 9
AnKi/Renderer/LightShading.cpp

@@ -106,12 +106,15 @@ Error LightShading::initSkybox()
 {
 	ANKI_CHECK(getResourceManager().loadResource("Shaders/LightShadingSkybox.ankiprog", m_skybox.m_prog));
 
-	ShaderProgramResourceVariantInitInfo variantInitInfo(m_skybox.m_prog);
-	variantInitInfo.addMutation("METHOD", 0);
-	const ShaderProgramResourceVariant* variant;
-	m_skybox.m_prog->getOrCreateVariant(variantInitInfo, variant);
+	for(U32 method = 0; method < 2; ++method)
+	{
+		ShaderProgramResourceVariantInitInfo variantInitInfo(m_skybox.m_prog);
+		variantInitInfo.addMutation("METHOD", method);
+		const ShaderProgramResourceVariant* variant;
+		m_skybox.m_prog->getOrCreateVariant(variantInitInfo, variant);
 
-	m_skybox.m_grProg = variant->getProgram();
+		m_skybox.m_grProgs[method] = variant->getProgram();
+	}
 
 	return Error::NONE;
 }
@@ -228,10 +231,36 @@ void LightShading::run(const RenderingContext& ctx, RenderPassWorkContext& rgrap
 	{
 		cmdb->setDepthCompareOperation(CompareOperation::EQUAL);
 
-		cmdb->bindShaderProgram(m_skybox.m_grProg);
+		const Bool isSolidColor = ctx.m_renderQueue->m_skybox.m_skyboxTexture == nullptr;
+
+		if(isSolidColor)
+		{
+			cmdb->bindShaderProgram(m_skybox.m_grProgs[0]);
+
+			const Vec4 color(ctx.m_renderQueue->m_skybox.m_solidColor, 0.0);
+			cmdb->setPushConstants(&color, sizeof(color));
+		}
+		else
+		{
+			cmdb->bindShaderProgram(m_skybox.m_grProgs[1]);
+
+			class
+			{
+			public:
+				Mat4 m_invertedViewProjectionJitter;
+				Vec3 m_cameraPos;
+				F32 m_padding = 0.0;
+			} pc;
+
+			pc.m_invertedViewProjectionJitter = ctx.m_matrices.m_invertedViewProjectionJitter;
+			pc.m_cameraPos = ctx.m_matrices.m_cameraTransform.getTranslationPart().xyz();
 
-		const Vec4 color(ctx.m_renderQueue->m_skybox.m_solidColor, 0.0);
-		cmdb->setPushConstants(&color, sizeof(color));
+			cmdb->setPushConstants(&pc, sizeof(pc));
+
+			cmdb->bindSampler(0, 0, m_r->getSamplers().m_trilinearRepeat);
+			cmdb->bindTexture(0, 1,
+							  TextureViewPtr(const_cast<TextureView*>(ctx.m_renderQueue->m_skybox.m_skyboxTexture)));
+		}
 
 		drawQuad(cmdb);
 
@@ -246,7 +275,7 @@ void LightShading::run(const RenderingContext& ctx, RenderPassWorkContext& rgrap
 
 		// Bind all
 		cmdb->bindSampler(0, 0, m_r->getSamplers().m_nearestNearestClamp);
-		cmdb->bindSampler(0, 1, m_r->getSamplers().m_trilinearClamp);
+		cmdb->bindSampler(0, 1, m_r->getSamplers().m_trilinearRepeatAnisoResolutionScalingBias);
 
 		rgraphCtx.bindTexture(0, 2, m_r->getGBuffer().getDepthRt(),
 							  TextureSubresourceInfo(DepthStencilAspectBit::DEPTH));

+ 1 - 1
AnKi/Renderer/LightShading.h

@@ -46,7 +46,7 @@ private:
 	{
 	public:
 		ShaderProgramResourcePtr m_prog;
-		ShaderProgramPtr m_grProg;
+		Array<ShaderProgramPtr, 2> m_grProgs;
 	} m_skybox;
 
 	class

+ 1 - 1
AnKi/Scene/Components/SkyboxComponent.cpp

@@ -29,7 +29,7 @@ void SkyboxComponent::setImage(CString filename)
 	const Error err = m_node->getSceneGraph().getResourceManager().loadResource(filename, m_image);
 	if(err)
 	{
-		ANKI_SCENE_LOGE("Setting skybox image failed");
+		ANKI_SCENE_LOGE("Setting skybox image failed. Ignoring error");
 	}
 	else
 	{

+ 25 - 0
AnKi/Shaders/Functions.glsl

@@ -620,3 +620,28 @@ UVec2 decodeVrsRate(U32 texel)
 	rateXY.y = 1u << (texel & 3u);
 	return rateXY;
 }
+
+/// 3D coordinates to equirectangular 2D coordinates.
+Vec2 equirectangularMapping(Vec3 v)
+{
+	Vec2 uv = Vec2(atan(v.z, v.x), asin(v.y));
+	uv *= vec2(0.1591, 0.3183);
+	uv += 0.5;
+	return uv;
+}
+
+Vec3 linearToSRgb(Vec3 linearRgb)
+{
+	const bvec3 cutoff = lessThan(linearRgb, Vec3(0.0031308));
+	const Vec3 higher = 1.055 * pow(linearRgb, Vec3(1.0 / 2.4)) - 0.055;
+	const Vec3 lower = linearRgb * 12.92;
+	return mix(higher, lower, cutoff);
+}
+
+Vec3 sRgbToLinear(Vec3 sRgb)
+{
+	const bvec3 cutoff = lessThan(sRgb, Vec3(0.04045));
+	const Vec3 higher = pow((sRgb + 0.055) / 1.055, Vec3(2.4));
+	const Vec3 lower = sRgb / 12.92;
+	return mix(higher, lower, cutoff);
+}

+ 4 - 0
AnKi/Shaders/LightShadingApplyIndirect.ankiprog

@@ -45,6 +45,10 @@ void main()
 
 	// Reference
 	const F32 depthCenter = textureLod(u_fullDepthTex, u_linearAnyClampSampler, in_uv, 0.0).x;
+	if(depthCenter == 1.0)
+	{
+		discard;
+	}
 
 	// Do a bilateral upscale
 	ANKI_RP Vec3 diffuse = Vec3(0.0);

+ 24 - 0
AnKi/Shaders/LightShadingSkybox.ankiprog

@@ -26,15 +26,39 @@ void main()
 layout(location = 0) in Vec2 in_uv;
 layout(location = 0) out ANKI_RP Vec3 out_color;
 
+#if METHOD == 0
 layout(push_constant, scalar) uniform b_pc
 {
 	ANKI_RP Vec3 u_solidColor;
 	F32 u_padding;
 };
+#else
+layout(binding = 0) uniform sampler u_trilinearAnySampler;
+layout(binding = 1) uniform ANKI_RP texture2D u_envMapTex;
+
+layout(push_constant, scalar, row_major) uniform b_pc
+{
+	Mat4 u_invertedViewProjectionJitter;
+	Vec3 u_cameraPos;
+	F32 u_padding;
+};
+#endif
 
 void main()
 {
+#if METHOD == 0
 	out_color = u_solidColor;
+#else
+	const F32 depth = 1.0;
+	const Vec2 ndc = UV_TO_NDC(in_uv);
+	const Vec4 worldPos4 = u_invertedViewProjectionJitter * Vec4(ndc, depth, 1.0);
+	const Vec3 worldPos = worldPos4.xyz / worldPos4.w;
+
+	const Vec3 eyeToFrag = normalize(worldPos - u_cameraPos);
+
+	const Vec2 uv = equirectangularMapping(eyeToFrag);
+	out_color = texture(u_envMapTex, u_trilinearAnySampler, uv).rgb;
+#endif
 }
 
 #pragma anki end

BIN
EngineAssets/DefaultSkybox.ankitex


+ 5 - 5
Samples/Sponza/Assets/Scene.lua

@@ -1,12 +1,12 @@
--- Generated by: /home/godlike/src/anki/buildd/Bin/GltfImporter sponza_crytek_7_pbr.gltf /home/godlike/src/anki/Samples/Sponza/Assets/ -rpath Assets -texrpath Assets -lod-count 2 -light-scale 0.01
+-- Generated by: /home/godlike/src/anki/buildd/Bin/GltfImporter sponza_crytek_7_pbr_3.0.gltf /home/godlike/src/anki/Samples/Sponza/Assets/ -rpath Assets -texrpath Assets -lod-count 2 -light-scale 0.01
 local scene = getSceneGraph()
 local events = getEventManager()
 
 node = scene:newSkyboxNode("Cube.017")
 comp = node:getSceneNodeBase():getSkyboxComponent()
-comp:setSolidColor(Vec3.new(1.67, 2.588, 4))
+comp:setImage("EngineAssets/DefaultSkybox.ankitex")
 trf = Transform.new()
-trf:setOrigin(Vec4.new(0.000000, 28.856024, 24.232458, 0))
+trf:setOrigin(Vec4.new(0.000000, 23.077700, 25.287918, 0))
 rot = Mat3x4.new()
 rot:setAll(1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000)
 trf:setRotation(rot)
@@ -15,9 +15,9 @@ node:getSceneNodeBase():getMoveComponent():setLocalTransform(trf)
 
 node = scene:newGlobalIlluminationProbeNode("Cube.011")
 comp = node:getSceneNodeBase():getGlobalIlluminationProbeComponent()
-comp:setBoxVolumeSize(Vec3.new(48.303688, 25.505352, 21.646244))
+comp:setBoxVolumeSize(Vec3.new(48.094303, 25.484703, 21.568947))
 trf = Transform.new()
-trf:setOrigin(Vec4.new(-1.008493, 12.464414, -0.581032, 0))
+trf:setOrigin(Vec4.new(-1.055059, 12.490639, -0.594131, 0))
 rot = Mat3x4.new()
 rot:setAll(1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000)
 trf:setRotation(rot)

BIN
Samples/Sponza/Assets/carpet.ankimesh


BIN
Samples/Sponza/Assets/carpet_lod1.ankimesh


+ 10 - 0
Tools/Image/ImageImporterMain.cpp

@@ -18,6 +18,8 @@ Options:
 -mip-count <number>    : Max number of mipmaps. By default store until 4x4
 -astc-block-size <XxY> : The size of the ASTC block size. eg 4x4. Default is 8x8
 -verbose               : Verbose log
+-to-linear             : Convert sRGB to linear
+-to-srgb               : Convert linear to sRGB
 )";
 
 static Error parseCommandLineArgs(int argc, char** argv, ImageImporterConfig& config,
@@ -166,6 +168,14 @@ static Error parseCommandLineArgs(int argc, char** argv, ImageImporterConfig& co
 		{
 			LoggerSingleton::get().enableVerbosity(true);
 		}
+		else if(CString(argv[i]) == "-to-linear")
+		{
+			config.m_sRgbToLinear = true;
+		}
+		else if(CString(argv[i]) == "-to-srgb")
+		{
+			config.m_linearToSRgb = true;
+		}
 		else
 		{
 			filenames.emplaceBack(filenames.getAllocator(), argv[i]);