This page has been translated automatically.
Video Tutorials
Interface
Essentials
Advanced
How To
Professional (SIM)
UnigineEditor
Interface Overview
Assets Workflow
Settings and Preferences
Working With Projects
Adjusting Node Parameters
Setting Up Materials
Setting Up Properties
Lighting
Sandworm
Using Editor Tools for Specific Tasks
Extending Editor Functionality
Built-in Node Types
Nodes
Objects
Effects
Decals
Light Sources
Geodetics
World Nodes
Sound Objects
Pathfinding Objects
Players
Programming
Fundamentals
Setting Up Development Environment
C++
C#
UnigineScript
UUSL (Unified UNIGINE Shader Language)
Plugins
File Formats
Materials and Shaders
Rebuilding the Engine Tools
GUI
Double Precision Coordinates
API
Containers
Common Functionality
Controls-Related Classes
Engine-Related Classes
Filesystem Functionality
GUI-Related Classes
Math Functionality
Node-Related Classes
Objects-Related Classes
Networking Functionality
Pathfinding-Related Classes
Physics-Related Classes
Plugins-Related Classes
IG Plugin
CIGIConnector Plugin
Rendering-Related Classes
Content Creation
Content Optimization
Materials
Material Nodes Library
Miscellaneous
Input
Math
Matrix
Textures
Art Samples
Tutorials
Warning! This version of documentation is OUTDATED, as it describes an older SDK version! Please switch to the documentation for the latest SDK version.
Warning! This version of documentation describes an old SDK version which is no longer supported! Please upgrade to the latest SDK version.

Creating Mirrors Using Viewports (Rendering to Texture) or a Standard Material

Car Rearview Mirrors

Car Rearview Mirrors

This example covers some aspects of using viewports and cameras as well as demonstrates 2 different ways of implementing mirrors:

  • Using the planar reflection option of the standard mesh_base material.
    Notice
    Planar reflections don't reflect each other and miss post-effects.
  • Using the Viewport class to render an image from a certain camera to the RenderTarget interface and then setting the texture as albedo texture of the mirror material.
Key features:
  • 3 rearview mirrors implemented using viewport texture rendering
  • large top rearview tilted mirror implemented using the planar reflection option of the mesh_base material.
  • 2 modes:
    • Single viewport for multiple cameras
    • Separate viewport for each camera
  • Keyboard control of car movement.

Using the Standard mesh_base Material#

You can create mirrors by simply using the planar reflection option the mesh_base material. The basic workflow here is as follows:

  1. Create a mesh, that is going to represent a mirror.
  2. Inherit a material from the mesh_base.
  3. For the new inherited material perform the following actions:
    • Enable the planar reflection option for the new inherited material.

    For metalness workflow:

    • Set the value of the metalness parameter to 1.
    • Set the value of the roughness parameter to 0.

    For specular workflow:

    • Set the value of the gloss parameter to 1.
    • Set the value of the microfiber parameter to 0.
  4. Tune the reflection_pivot_rotation parameter to compensate rotation of the surface if any.
  5. Assign the new inherited material to the reflecting surface.
  6. Enable rendering of planar reflections using the render_reflection_dynamic console command.
This approach is implemented here in the create_top_mirror() method, that creates the top rearview tilted mirror.

Using Viewports (Rendering to Texture)#

Another way of creating mirrors is to use a viewport to render an image from a camera placed behind the mirror to a texture and set it as albedo texture for the material of the reflecting surface. This approach can also be used to create various portals or monitors showing an image from a certain camera etc. The basic workflow here is as follows:

  1. Create a mesh, that is going to represent a mirror.
  2. Inherit a material from the mesh_base.
  3. Create a camera and set its projection and modelview matrices (in case of implementing a mirror the camera should be placed behind the mirror looking in the direction of the normal to the reflecting surface).
  4. Create a 2D texture to render the image from the camera to.
  5. Create a viewport to render the image from the camera to the texture.
    Notice
    It is recommended to use a separate viewport for each camera. In case of using several cameras with a single viewport rendering of screen-space effects should be disabled to avoid artefacts (See the render_screen_space_effects console command).
  6. Save current render state and change necessary settings.
  7. Render an image from the camera to the texture using the renderTexture2D() method of the Viewport class.
  8. Restore the previously saved render state.
  9. Set the texture as albedo texture for the material of the reflecting surface.
This approach is implemented here for all three small rearview mirrors.

Using Both Approaches to Create Mirrors for a Car#

Create a C# component and add the following code to it:

Source code (C#)
using System;
using System.Collections;
using System.Collections.Generic;
using Unigine;

#region Math Variables
#if UNIGINE_DOUBLE
using Vec3 = Unigine.dvec3;
#else
using Vec3 = Unigine.vec3;
#endif
#endregion

[Component(PropertyGuid = "AUTOGENERATED_GUID")] // <-- this line is generated automatically for a new component
public class CarWithMirrors : Component
{
	enum VMode
	{
		MODE_VIEWPORT_SINGLE,   // use a single viewport for multiple cameras
		MODE_VIEWPORT_MULTIPLE  // use a separate viewport for each camera
	};

	// rearview mirror 
	struct Mirror
	{
		public ObjectMeshStatic mesh;                   // mirror mesh
		public String m_type;                           // mirror type (left/right/mid)
		public Texture texture;                         // mirror texture
		public PlayerDummy camera;                      // mirror camera
		public Viewport viewport;                       // mirror viewport
		public Vec3 position;                          // mirror position
	};

	const float MOVING_SPEED = 10.0f;               // speed of objects movement
	const float DELTA_ANGLE = 60.0f;                // delta angle of objects rotation
	const int ASPECT_RATIO = 1;                     // aspect ratio
	const float HFOV = 60;                          // horizontal field of view
	const float VFOV = HFOV / ASPECT_RATIO;         // vertical field of view
	VMode MODE = VMode.MODE_VIEWPORT_MULTIPLE;      // set viewport mode (single/multiple)

	ObjectMeshStatic car_frame;
	ObjectMeshStatic material_mirror;
	Mirror[] mirrors = new Mirror[3];
	Controls controls;
	float ifps;

	///  method creating a mirror using the mesh_base "planar reflection" option
	public bool create_top_mirror()
	{
		// creating a mesh with a plane surface for the top mirror
		Mesh mesh = new Mesh();
		mesh.AddPlaneSurface("plane", 1.97f, 0.3f, 1.0f);

		// rotating the mesh surface by 108 degrees around the X-axis
		mesh.SetSurfaceTransform(MathLib.RotateX(108.0f));

		// creating a top mirror and setting its position
		material_mirror = new ObjectMeshStatic(mesh);
		material_mirror.Position = new Vec3(0.0f, 0.73f, 1.60f);

		// passing the node to the Editor and adding it as a child to the car frame
		car_frame.AddChild(material_mirror);

		// creating a new mirror material named "planar_reflector"
		Material mesh_base = Materials.FindManualMaterial("Unigine::mesh_base");
		Material reflector_material = mesh_base.Inherit();

		// enabling planar reflections for the mirror material
		reflector_material.SetState("planar_reflection", 1);

		// setting metallness and roughness parameters to make the surface look like a mirror
		reflector_material.SetParameterFloat(reflector_material.FindParameter("metalness"), 1.0f);
		reflector_material.SetParameterFloat(reflector_material.FindParameter("roughness"), 0.0f);

		// compensating the 108-degree rotation angle of the mesh surface
		reflector_material.SetParameterFloat4(reflector_material.FindParameter("reflection_pivot_rotation"), new vec4(108.0f, 0.0f, 0.0f, 0.0f));

		// assigning new mirror material to the surface of the mirror
		material_mirror.SetMaterial(reflector_material, 0);

		// enabling planar reflections rendering using the corresponding console command (render_reflection_dynamic)
		Unigine.Console.Run("render_reflection_dynamic 1");

		return true;
	}
	

	private void Init()
	{
		// setting texture width and height
		int width = 512;
		int height = width / ASPECT_RATIO;

		// preparing a mesh for the car body
		Mesh bodymesh = new Mesh();
		bodymesh.AddBoxSurface("body", new vec3(2.0f, 5.0f, 1.2f));
		bodymesh.AddBoxSurface("body", new vec3(0.05f, 0.05f, 1.1f));
		bodymesh.SetSurfaceTransform(MathLib.Translate(new vec3(0.96f, 0.8f, 0.91f)), 1);
		bodymesh.AddBoxSurface("body", new vec3(0.05f, 0.05f, 1.1f));
		bodymesh.SetSurfaceTransform(MathLib.Translate(new vec3(-0.96f, 0.8f, 0.91f)), 2);
		bodymesh.AddBoxSurface("body", new vec3(1.88f, 0.05f, 0.05f));
		bodymesh.SetSurfaceTransform(MathLib.Translate(new vec3(0.0f, 0.8f, 1.435f)), 3);

		// creating car body using a mesh and passing it to the Editor
		car_frame = new ObjectMeshStatic(bodymesh);
		car_frame.SetMaterialParameterFloat4("albedo_color", new vec4(1.0f, 0.0f, 0.0f, 1.0f), 0);

		// creating a top rearview mirror using the mesh_base material
		create_top_mirror();

		// setting type and position for all mirrors
		mirrors[0].m_type = "left";
		mirrors[1].m_type = "mid";
		mirrors[2].m_type = "right";
		mirrors[0].position = new Vec3(-1.08f, 0.77f, 0.9f);
		mirrors[1].position = new Vec3(0.0f, 0.35f, 1.25f);
		mirrors[2].position = new Vec3(1.08f, 0.77f, 0.9f);

		// initializing mirrors
		for (int i = 0; i < 3; i++)
		{
			// creating a mesh for the current mirror
			Mesh mesh = new Mesh();
			mesh.AddPlaneSurface("mirror_plane", 0.2f, 0.1f, 1);

			// creating the current mirror and setting its transform
			mirrors[i].mesh = new ObjectMeshStatic(mesh);
			mirrors[i].mesh.Transform = new mat4(MathLib.Translate(new vec3(mirrors[i].position)) * MathLib.RotateX(90.0f));

			// creating and setting materials for the current mirror mesh
			Material mesh_base = Materials.FindManualMaterial("Unigine::mesh_base");
			Material car_mirror = mesh_base.Inherit();
			mirrors[i].mesh.SetMaterial(car_mirror, 0);

			// creating a camera for the current mirror and setting its parameters
			mirrors[i].camera = new PlayerDummy();
			mirrors[i].camera.Projection = MathLib.Perspective(VFOV, 2.0f, 0.05f, 10000.0f) * MathLib.Scale(-1.0f, 1.0f, 1.0f);
			mirrors[i].camera.Position = mirrors[i].position;
			mirrors[i].camera.SetDirection(new vec3(0.0f, -1.0f, 0.0f), vec3.UP);

			// adding mirror mesh and camera as children to the car frame 
			car_frame.AddChild(mirrors[i].mesh);
			car_frame.AddChild(mirrors[i].camera);

			// creating a 2D texture to be set for the mirror material
			mirrors[i].texture = new Texture();

			mirrors[i].texture.Create2D(width, height, Texture.FORMAT_RGBA8, Texture.FORMAT_USAGE_RENDER);

			// checking viewport mode and creating necessary number of viewports
			if (MODE == VMode.MODE_VIEWPORT_MULTIPLE)
			{
				mirrors[i].viewport = new Viewport();
			} else if ((MODE == VMode.MODE_VIEWPORT_SINGLE) && (i == 1))
			{
				mirrors[i].viewport = new Viewport();
			}
		}

		// setting up player and controls
		PlayerSpectator player = new PlayerSpectator();
		car_frame.AddChild(player);
		player.WorldPosition = new vec3(0.0f, -0.5f, 1.1f);
		player.Controlled = false;
		Game.Player = player;
		Game.Enabled = true;
		player.Fov = 60.0f;
		player.SetDirection(new vec3(0.0f, 1.0f, 0.0f), new vec3(0.0f, 0.0f, -1.0f));
		player.Collision = 0;
		controls = player.Controls;

		car_frame.WorldPosition = car_frame.WorldPosition + new vec3(0.0f, 4.0f, 0.0f);
		
	}
	
	private void Update()
	{
		ifps = Engine.IFps;
		// get the current world transformation matrix of the mesh
		dmat4 transform = car_frame.WorldTransform;

		// get the direction vector of the mesh from the second column of the transformation matrix
		Vec3 direction = transform.GetColumn3(1);

		// checking controls states and changing car frame transformation
		if ((controls.GetState(Controls.STATE_FORWARD) == 1) || (controls.GetState(Controls.STATE_TURN_UP) == 1))
		{
			car_frame.WorldPosition = car_frame.WorldPosition + direction * MOVING_SPEED * ifps;
		}
		if ((controls.GetState(Controls.STATE_BACKWARD) == 1) || (controls.GetState(Controls.STATE_TURN_DOWN) == 1))
		{
			car_frame.WorldPosition = car_frame.WorldPosition - direction * MOVING_SPEED * ifps;
		}
		if ((controls.GetState(Controls.STATE_MOVE_LEFT) == 1) || (controls.GetState(Controls.STATE_TURN_LEFT) == 1))
		{
			car_frame.SetWorldRotation(car_frame.GetWorldRotation() * new quat(0.0f, 0.0f, DELTA_ANGLE * ifps));
			direction.z += DELTA_ANGLE * ifps;
		}
		if ((controls.GetState(Controls.STATE_MOVE_RIGHT) == 1) || (controls.GetState(Controls.STATE_TURN_RIGHT) == 1))
		{
			car_frame.SetWorldRotation(car_frame.GetWorldRotation() * new quat(0.0f, 0.0f, -DELTA_ANGLE * ifps));
			direction.z -= DELTA_ANGLE * ifps;
		}
		
	}

	private void PostUpdate()
	{
		Viewport viewport;

		for (int i = 0; i < 3; i++)
		{
			if (MODE == VMode.MODE_VIEWPORT_MULTIPLE)
			{
				// using a separate viewport for each camera
				viewport = mirrors[i].viewport;
			} else
			{
				// using a single viewport for all cameras
				viewport = mirrors[1].viewport;

				// skipping post effects when using a single viewport for multiple cameras to avoid artefacts
				viewport.AppendSkipFlags(Viewport.SKIP_POSTEFFECTS);
			}

			// saving current render state and clearing it 
			RenderState.SaveState();
			RenderState.ClearStates();

			// enabling polygon front mode to correct camera flipping
			RenderState.PolygonFront = 1;

			// rendering and image from the camera of the current mirror to the texture
			viewport.RenderTexture2D(mirrors[i].camera.Camera, mirrors[i].texture);

			// restoring back render state
			RenderState.PolygonFront = 0;
			RenderState.RestoreState();

			Material material = mirrors[i].mesh.GetMaterialInherit(0);
			// setting rendered texture as albedo texture for the material of the current mirror
			material.SetTexture(material.FindTexture("albedo"), mirrors[i].texture);
		}
	}
}
Last update: 2022-11-04
Build: ()