forked from BilalY/Rasagar
171 lines
8.3 KiB
C#
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();
|
|
}
|
|
}
|
|
}
|