Rasagar/Library/PackageCache/com.unity.render-pipelines.high-definition/Editor/RayTracing/SolidAngleKernelGenerator.cs
2024-08-26 23:07:20 +03:00

171 lines
8.3 KiB
C#

using UnityEditor;
using UnityEngine.Experimental.Rendering;
namespace UnityEngine.Rendering.HighDefinition
{
internal class SolidAngleKernelGenerator : MonoBehaviour
{
public class CameraParameters
{
public int width;
public int height;
public float fov;
public float aspect;
public float nearPlane;
public float farPlane;
public float maxCameraDistance;
public float maxVirtualPlaneDistance;
public float planeAngle;
}
float SafeDiv(float numer, float denom)
{
return (numer != denom) ? numer / denom : 1;
}
float D_GGXNoPI(float NdotH, float roughness)
{
float a2 = roughness * roughness;
float s = (NdotH * a2 - NdotH) * NdotH + 1.0f;
// If roughness is 0, returns (NdotH == 1 ? 1 : 0).
// That is, it returns 1 for perfect mirror reflection, and 0 otherwise.
return SafeDiv(a2, s * s);
}
public void GenerateTableExample()
{
CameraParameters cameraParameters = new CameraParameters();
cameraParameters.width = 1980;
cameraParameters.height = 1080;
cameraParameters.fov = 70.0f;
cameraParameters.aspect = 1980.0f / 1080.0f;
cameraParameters.nearPlane = 0.01f;
cameraParameters.farPlane = 100.0f;
cameraParameters.maxVirtualPlaneDistance = 10.0f;
cameraParameters.maxCameraDistance = 50.0f;
cameraParameters.planeAngle = 45.0f;
int angleSubdivision = 16;
float brdfPercentage = 0.7f;
int outputWidth = 32;
int outputHeight = 32;
int outputDepth = 64;
GenerateTable(cameraParameters, angleSubdivision, brdfPercentage, outputWidth, outputHeight, outputDepth);
}
void GetLocalFrame(Vector3 localZ, Vector3 localX, out Vector3 localY)
{
localY = Vector3.Cross(localZ, localX);
}
bool intersectPlane(Vector3 n, Vector3 p0, Vector3 l0, Vector3 l, ref float t)
{
float denom = Vector3.Dot(n, l);
if (Mathf.Abs(denom) > 1e-6)
{
Vector3 p0l0 = p0 - l0;
t = Vector3.Dot(p0l0, n) / denom;
return (t >= 0);
}
return false;
}
public void GenerateTable(CameraParameters cameraParameters, int angleSubdivision, float brdfPercentage, int outputWidth, int outputHeight, int outputDepth)
{
// Allocate our output texture
Texture3D kernelSize = new Texture3D(outputWidth, outputHeight, outputDepth, GraphicsFormat.R32G32B32A32_SFloat, TextureCreationFlags.None);
// First of all, let's compute the projection matrix
Matrix4x4 cameraProjection = Matrix4x4.Perspective(cameraParameters.fov, cameraParameters.aspect, cameraParameters.nearPlane, cameraParameters.farPlane);
// This buffer holds the mapping between the (rayAngle, solidAngle, distance) -> (with, height) (normalized by 32 and clamped)
float[] outputFilter = new float[outputWidth * outputHeight * outputDepth * 2];
// Color used to fill the tex3d
Color currentColor = new Color();
// We loop through all the camera distance values
for (int currentCameraDistanceIdx = 0; currentCameraDistanceIdx < outputDepth; ++currentCameraDistanceIdx)
{
// Compute the distance between the plane an the camera
float cameraDistance = cameraParameters.maxCameraDistance * (currentCameraDistanceIdx + 1) / (float)outputDepth;
// Compute the position of the point
Vector3 pointPosition = new Vector3(0.0f, 0.0f, cameraDistance);
// Loop through the solid angle values
for (int currentSolidAngleIdx = 0; currentSolidAngleIdx < outputHeight; ++currentSolidAngleIdx)
{
// Evaluate the current angle value
float currentSolidAngle = Mathf.PI * 0.5f * currentSolidAngleIdx / (float)outputHeight;
// Loop through all the point -> virtual plane distances
for (int currentVirtualDistanceIdx = 0; currentVirtualDistanceIdx < outputWidth; ++currentVirtualDistanceIdx)
{
// Evaluate the current angle value
float currentVirtualDistance = cameraParameters.maxVirtualPlaneDistance * currentVirtualDistanceIdx / (float)outputWidth;
// Let's compute the rotated normal (takes degrees)
Vector3 normalVector = Quaternion.AngleAxis(cameraParameters.planeAngle, Vector3.right) * -Vector3.forward;
// Let's compute the local to world matrix
Vector3 localX = new Vector3(1.0f, 0.0f, 0.0f);
Vector3 localY = new Vector3();
GetLocalFrame(normalVector, localX, out localY);
int minWidth = int.MaxValue;
int minHeight = int.MaxValue;
int maxWidth = -int.MaxValue;
int maxHeight = -int.MaxValue;
// Then we loop through the points that are in the circle that matches the cone angle
for (int phiIdx = 0; phiIdx < angleSubdivision; ++phiIdx)
{
// Compute the current phi value
float phi = Mathf.PI * 2.0f * phiIdx / (float)angleSubdivision;
// Generate the direction in local space (reflected dir space)
Vector3 sampleDir = new Vector3(Mathf.Sin(currentSolidAngle) * Mathf.Cos(phi), Mathf.Sin(currentSolidAngle) * Mathf.Sin(phi), Mathf.Cos(currentSolidAngle));
// Move it to world space
sampleDir = sampleDir.x * localX + sampleDir.y * localY + sampleDir.z * normalVector;
// Compute the position on the virtual 1 meter plane
Vector3 virtualPoint = currentVirtualDistance * sampleDir + pointPosition;
// Then we need to project it along the reflected direction
float tx = -1.0f;
if (intersectPlane(normalVector, pointPosition, virtualPoint, -normalVector, ref tx))
{
Vector3 pointProject = virtualPoint - normalVector * tx;
// Project the point back to near plane.
Vector4 pointW = cameraProjection * new Vector4(pointProject.x, pointProject.y, pointProject.z, 1.0f);
pointW.x /= pointW.w;
pointW.y /= pointW.w;
pointW.x = pointW.x * 0.5f + 0.5f;
pointW.y = pointW.y * 0.5f + 0.5f;
minWidth = Mathf.Min(minWidth, (int)(pointW.x * cameraParameters.width));
maxWidth = Mathf.Max(maxWidth, (int)(pointW.x * cameraParameters.width));
minHeight = Mathf.Min(minHeight, (int)(pointW.y * cameraParameters.height));
maxHeight = Mathf.Max(maxHeight, (int)(pointW.y * cameraParameters.height));
}
}
float kernelSizeX = Mathf.Clamp((maxWidth - minWidth) / (float)32.0f, 0.0f, 1.0f);
float kernelSizeY = Mathf.Clamp((maxHeight - minHeight) / (float)32.0f, 0.0f, 1.0f);
currentColor.r = kernelSizeX;
currentColor.g = kernelSizeY;
currentColor.b = 0.0f;
currentColor.a = 1.0f;
kernelSize.SetPixel(currentVirtualDistanceIdx, currentSolidAngleIdx, currentCameraDistanceIdx, currentColor);
}
}
}
// Save our texture to file
AssetDatabase.CreateAsset(kernelSize, "Assets/ShadowFilterMapping.asset");
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
}
}