How-to: Use Physically Based Rendering

Physically Based Rendering is a set of rendering techniques which simulate how light and surface materials interact in the natural world. The goal is to create images which are more realistic than traditional shading approaches.

HPS

This is a very simple HPS sample to create a shell using Physically Based Rendering (or PBR).

PBR uses UV source parameterization and expects explicit parameterization of the shells using such a material. More details are available about PBR and related properties under the Materials documentation.

Instructions

  1. Add the following code to a CHPSView User Code method in the MFC Sandbox application:
void CHPSView::OnUserCode3()
{
	PortfolioKey portfolio = Database::CreatePortfolio();
	SegmentKey my_seg = GetCanvas().GetFrontView().GetAttachedModel().GetSegmentKey();
	my_seg.GetPortfolioControl().Push(portfolio);
	{
		auto load_png = [&](const char * file_name, const char * image_name) -> bool {
			HPS::Image::ImportOptionsKit iok;
			iok.SetFormat(HPS::Image::Format::Png);
			ImageKit image_kit = Image::File::Import(file_name, iok);
			ImageDefinition image_def = portfolio.DefineImage(image_name, image_kit);
			TextureDefinition txr_def = portfolio.DefineTexture(image_name, image_def);
			return true;
		};

		load_png("ship.png", "ship");

		load_png("IMAGE_PBR_PATH/chipped-paint-metal/chipped-paint-metal-albedo.png", "chipped-paint-metal-albedo");
		load_png("IMAGE_PBR_PATH/chipped-paint-metal/chipped-paint-ao.png", "chipped-paint-metal-ao");
		load_png("IMAGE_PBR_PATH/chipped-paint-metal/chipped-paint-metal-metal.png", "chipped-paint-metal-metalness");
		load_png("IMAGE_PBR_PATH/chipped-paint-metal/chipped-paint-metal-rough2.png", "chipped-paint-metal-roughness");
		load_png("IMAGE_PBR_PATH/chipped-paint-metal/chipped-paint-metal-normal-dx.png", "chipped-paint-metal-normal");

		GlyphKit glyphKit = GlyphKit();
		glyphKit.SetRadius(0);
		glyphKit.SetOffset(GlyphPoint(0, 0));

		ImageGlyphElement imageGlyphElement;
		imageGlyphElement.SetSource("ship");

		GlyphElementArray glyphElements;
		glyphElements.push_back(imageGlyphElement);
		glyphKit.SetElements(glyphElements);

		portfolio.DefineGlyph("shipGlyph", glyphKit);
	}
	SegmentKey shellSegment = my_seg.Subsegment();
	shellSegment.GetVisibilityControl().SetEverything(false);

	// Insert a small test shell with parameterization
	ShellKey shellKey;
	{
		float const quad_points[] =
		{
			-0.75f, 0.75f, 0.0f,
			0.75f, 0.75f, 0.0f,
			0.75f, -0.75f, 0.0f,
			-0.75f, -0.75f, 0.0f
		};

		int const quad_flist[] =
		{
			4, 0, 1, 2, 3
		};

		float const quad_uvs[] =
		{
			0.0f, 1.0f,
			1.0f, 1.0f,
			1.0f, 0.0f,
			0.0f, 0.0f,
		};

		shellKey = shellSegment.InsertShell(4, (Point const *)&quad_points[0], 5, quad_flist);
		shellKey.SetVertexParametersByRange(0, 8, quad_uvs, 2);
	}

	my_seg.GetVisibilityControl().SetEverything(false).SetFaces(true).SetLights(true);
	my_seg.ReferenceGeometry(shellKey);

	PBRMaterialKit pbr;
	pbr.SetBaseColorMap("chipped-paint-metal-albedo");
	pbr.SetNormalMap("chipped-paint-metal-normal");
	pbr.SetMetalnessMap("chipped-paint-metal-metalness", HPS::Material::Texture::ChannelMapping::Red);
	pbr.SetRoughnessMap("chipped-paint-metal-roughness", HPS::Material::Texture::ChannelMapping::Red);
	pbr.SetOcclusionMap("chipped-paint-metal-ao", HPS::Material::Texture::ChannelMapping::Red);

	my_seg.SetPBRMaterial(pbr);

	my_seg.InsertDistantLight(Vector(0.4174f, 0.2564f, -0.8951f));
}
  1. Download the Chipped Paint Metal PBR Material images archive from FreePBR.com called chipped-paint-metal-ue and install it on your system.
  2. Replace “IMAGE_PBR_PATH” in the code with the actual path to the unzipped Chipped Paint Metal files.

This should result in a scene similar to:

3DF

This is a very simple 3DF sample function to create a shell using Physically Based Rendering (or PBR).

PBR uses UV source parameterization and expects explicit parameterization of the shells using such a material. More details are available about PBR and related properties under the Materials documentation.

Instructions

  1. Add the following test function to a HOOPS 3DF application:
int TestPBR ()
{
	if (!IsShaderDriver()) // make sure driver is ogl or dx11
		return TEST_UNSUPPORTED;

    HC_Open_Segment ("a");
		HC_Open_Segment("image"); {
			HC_Set_Visibility("images=off");
			auto load_png = [](const char * file_name, const char * image_name) -> bool {
				FILE *file = fopen(file_name), "rb");
				if (!file)
					return false;
				fseek(file, 0, SEEK_END);
				int length = ftell(file);
				fseek(file, 0, SEEK_SET);
				unsigned char * buffer = new unsigned char[length];
				fread(buffer, sizeof(unsigned char), length, file);
				fclose(file);
				HC_Insert_Compressed_Image(0, 0, 0, H_FORMAT_TEXT("png, name = %s", image_name), -1, -1, length, buffer);
				delete[] buffer;
				return true;
			};

			if (!load_png("IMAGE_PBR_PATH/sandstonecliff-albedo.png", "sandstonecliff-albedo"))
				return TEST_IMAGE_MISSING;
			if (!load_png("IMAGE_PBR_PATH/sandstonecliff-ao.png", "sandstonecliff-ao"))
				return TEST_IMAGE_MISSING;
			if (!load_png("IMAGE_PBR_PATH/sandstonecliff-metalness.png", "sandstonecliff-metalness"))
				return TEST_IMAGE_MISSING;
			if (!load_png("IMAGE_PBR_PATH/sandstonecliff-roughness.png", "sandstonecliff-roughness"))
				return TEST_IMAGE_MISSING;
			if (!load_png("IMAGE_PBR_PATH/sandstonecliff-normal-ue.png", "sandstonecliff-normal"))
				return TEST_IMAGE_MISSING;
		} HC_Close_Segment();

		HC_Set_PBR_Material("sandstonecliff-albedo", "sandstonecliff-normal", nullptr,
							"sandstonecliff-metalness", 0,
							"sandstonecliff-roughness", 0,
							"sandstonecliff-ao", 0,
							nullptr,
							1.0, 1.0, 1.0, 1.0, 1.0,
							nullptr);
		HC_Insert_Distant_Light(1, 1, -1);

		HC_Open_Segment("shell"); {
			float const quad_points[] = {
				-0.75f, 0.75f, 0.0f,
				0.75f, 0.75f, 0.0f,
				0.75f, -0.75f, 0.0f,
				-0.75f, -0.75f, 0.0f
			};
			int const quad_flist[] = {4, 0, 1, 2, 3};
			float const quad_uvs[] = {
				0.0f, 1.0f,
				1.0f, 1.0f,
				1.0f, 0.0f,
				0.0f, 0.0f,
			};

			HC_Set_Visibility("no edges, no markers");
			HC_KEY	shell_key = HC_Insert_Shell(4, quad_points, 5, quad_flist);
			HC_MSet_Vertex_Parameters(shell_key, 0, 4, 2, quad_uvs);
		} HC_Close_Segment();
	
    HC_Close_Segment();

    return TEST_COMPLETE;
}
  1. Download the Sand Stone Cliff images archive from FreePBR.com called sandstonecliff-ue and install it on your system.
  2. Replace “IMAGE_PBR_PATH” in the code with the actual path to the unzipped Sand Stone Cliff files.

Do not worry about IsShaderDriver() or the specific return values.

This should result in a scene similar to:

PBR Materials Without Textures

It’s also possible to specify PBR materials without using textures or maps. Consider the following sample code;

HPS

void CHPSView::OnUserCode4()
{
	float sphereRadius = 0.8;
	float sphereOffset = 2.0;
	float factorOffset = 0.2;
	int factorCount = 7;
	float normalFactor = 0.0;
	float metalFactor = 0.0;
	float roughFactor = 0.0;
	float occlusionFactor = 0.0;
	float alphaFactor = 0.0;
	HPS::Point position(0,0,0);
	char segmentName[64];
	RGBAColor cyan(0, 1, 1, 1);

	SegmentKey testKey = GetCanvas().GetFrontView().GetAttachedModel().GetSegmentKey().Subsegment("test");
	testKey.InsertDistantLight(Vector(-0.4174f, -0.2564f, 0.8951f));

	sprintf(segmentName, "sphere baseline");
	SegmentKey baselineSphereKey = testKey.Subsegment(segmentName);
	baselineSphereKey.InsertSphere(position, sphereRadius);
	baselineSphereKey.GetMaterialMappingControl().SetFaceColor(HPS::RGBAColor(0, 1, 1));

	SegmentKey spheresKey = testKey.Subsegment("spheres");

	for (int i = 0; i < factorCount; ++i)
	{
		for (int j = 0; j < factorCount; ++j)
		{
			sprintf(segmentName, "sphere %d %d", i, j);
			position.x += sphereOffset;
			metalFactor = i * factorOffset;
			roughFactor = j * factorOffset;
			SegmentKey aSphereKey = spheresKey.Subsegment(segmentName);
			aSphereKey.InsertSphere(position, sphereRadius);
			HPS::PBRMaterialKit pbrMaterialKit;
			pbrMaterialKit.
				SetBaseColorFactor(cyan).
				SetNormalFactor(normalFactor).
				SetMetalnessFactor(metalFactor).
				SetRoughnessFactor(roughFactor).
				SetOcclusionFactor(occlusionFactor).
				SetAlphaFactor(alphaFactor);
			aSphereKey.SetPBRMaterial(pbrMaterialKit);
		}
		position.x = 0;
		position.y += sphereOffset;
	}
}

3DF

void TestSimplePBR()
{
	typedef struct { float r, g, b, a; } RGBA;
	float sphereRadius = 0.8;
	float sphereOffset = 2.0;
	float factorOffset = 0.2;
	int factorCount = 7;
	float normalFactor = 0.0;
	float metalFactor = 0.0;
	float roughFactor = 0.0;
	float occlusionFactor = 0.0;
	float alphaFactor = 0.0;
	HPoint position;
	char segmentName[64];
	RGBA cyan;
	cyan.r = 0;
	cyan.g = 1;
	cyan.b = 1;
	cyan.a = 1;

	HC_Open_Segment("test"); {

		sprintf(segmentName, "sphere baseline");
		position.Set(0, 0, 0);
		HC_Open_Segment(segmentName); {
			HC_Insert_Sphere(&position, sphereRadius, nullptr, nullptr);
			HC_Set_Color("faces = (r=0.0 g=1 b=1)");
		} HC_Close_Segment();

		HC_Open_Segment("spheres"); {
			for (int i = 0; i < factorCount; ++i)
			{
				for (int j = 0; j < factorCount; ++j)
				{
					sprintf(segmentName, "sphere %d %d", i, j);
					position.x += sphereOffset;
					metalFactor = i * factorOffset;
					roughFactor = j * factorOffset;
					HC_Open_Segment(segmentName); {
						HC_Insert_Sphere(&position, sphereRadius, nullptr, nullptr);
						HC_Set_PBR_Material(nullptr, nullptr, nullptr, nullptr, 0, nullptr, 0, nullptr, 0, &cyan, normalFactor, metalFactor, roughFactor, occlusionFactor, alphaFactor, nullptr);
					} HC_Close_Segment();
				}
				position.x = 0;
				position.y += sphereOffset;
			}
		} HC_Close_Segment();
	} HC_Close_Segment();
}

Rendering Result

This code produces a scene as follows where the rough factor changes from 0.0 to 1.0 moving from left to right and the metal factor change from 0.0 to 1.0 moving from the bottom to the top;

The bottom left sphere is a baseline using regular color for comparison.