added area lights
This commit is contained in:
@@ -691,6 +691,7 @@ void EditorComponent::Load()
|
||||
pointLightTex = *(Texture2D*)Content.add("images/pointlight.dds");
|
||||
spotLightTex = *(Texture2D*)Content.add("images/spotlight.dds");
|
||||
dirLightTex = *(Texture2D*)Content.add("images/directional_light.dds");
|
||||
areaLightTex = *(Texture2D*)Content.add("images/arealight.dds");
|
||||
}
|
||||
void EditorComponent::Start()
|
||||
{
|
||||
@@ -1162,6 +1163,9 @@ void EditorComponent::Compose()
|
||||
case Light::DIRECTIONAL:
|
||||
wiImage::Draw(&dirLightTex, fx, GRAPHICSTHREAD_IMMEDIATE);
|
||||
break;
|
||||
default:
|
||||
wiImage::Draw(&areaLightTex, fx, GRAPHICSTHREAD_IMMEDIATE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class EditorComponent
|
||||
: public TiledForwardRenderableComponent
|
||||
{
|
||||
private:
|
||||
wiGraphicsTypes::Texture2D pointLightTex, spotLightTex, dirLightTex;
|
||||
wiGraphicsTypes::Texture2D pointLightTex, spotLightTex, dirLightTex, areaLightTex;
|
||||
public:
|
||||
MaterialWindow* materialWnd;
|
||||
PostprocessWindow* postprocessWnd;
|
||||
|
||||
@@ -10,7 +10,7 @@ LightWindow::LightWindow(wiGUI* gui) : GUI(gui), light(nullptr)
|
||||
float screenH = (float)wiRenderer::GetDevice()->GetScreenHeight();
|
||||
|
||||
lightWindow = new wiWindow(GUI, "Light Window");
|
||||
lightWindow->SetSize(XMFLOAT2(400, 420));
|
||||
lightWindow->SetSize(XMFLOAT2(400, 500));
|
||||
//lightWindow->SetEnabled(false);
|
||||
GUI->AddWidget(lightWindow);
|
||||
|
||||
@@ -44,6 +44,45 @@ LightWindow::LightWindow(wiGUI* gui) : GUI(gui), light(nullptr)
|
||||
distanceSlider->SetTooltip("Adjust the maximum range the light can affect.");
|
||||
lightWindow->AddWidget(distanceSlider);
|
||||
|
||||
radiusSlider = new wiSlider(0.01f, 100, 0, 100000, "Radius: ");
|
||||
radiusSlider->SetSize(XMFLOAT2(100, 30));
|
||||
radiusSlider->SetPos(XMFLOAT2(x, y += step));
|
||||
radiusSlider->OnSlide([&](wiEventArgs args) {
|
||||
if (light != nullptr)
|
||||
{
|
||||
light->radius = args.fValue;
|
||||
}
|
||||
});
|
||||
radiusSlider->SetEnabled(false);
|
||||
radiusSlider->SetTooltip("Adjust the radius of an area light.");
|
||||
lightWindow->AddWidget(radiusSlider);
|
||||
|
||||
widthSlider = new wiSlider(1, 100, 0, 100000, "Width: ");
|
||||
widthSlider->SetSize(XMFLOAT2(100, 30));
|
||||
widthSlider->SetPos(XMFLOAT2(x, y += step));
|
||||
widthSlider->OnSlide([&](wiEventArgs args) {
|
||||
if (light != nullptr)
|
||||
{
|
||||
light->width = args.fValue;
|
||||
}
|
||||
});
|
||||
widthSlider->SetEnabled(false);
|
||||
widthSlider->SetTooltip("Adjust the width of an area light.");
|
||||
lightWindow->AddWidget(widthSlider);
|
||||
|
||||
heightSlider = new wiSlider(1, 100, 0, 100000, "Height: ");
|
||||
heightSlider->SetSize(XMFLOAT2(100, 30));
|
||||
heightSlider->SetPos(XMFLOAT2(x, y += step));
|
||||
heightSlider->OnSlide([&](wiEventArgs args) {
|
||||
if (light != nullptr)
|
||||
{
|
||||
light->height = args.fValue;
|
||||
}
|
||||
});
|
||||
heightSlider->SetEnabled(false);
|
||||
heightSlider->SetTooltip("Adjust the height of an area light.");
|
||||
lightWindow->AddWidget(heightSlider);
|
||||
|
||||
fovSlider = new wiSlider(0.1f, XM_PI - 0.01f, 0, 100000, "FOV: ");
|
||||
fovSlider->SetSize(XMFLOAT2(100, 30));
|
||||
fovSlider->SetPos(XMFLOAT2(x, y += step));
|
||||
@@ -133,6 +172,7 @@ LightWindow::LightWindow(wiGUI* gui) : GUI(gui), light(nullptr)
|
||||
if (light != nullptr && args.iValue >= 0)
|
||||
{
|
||||
light->type = (Light::LightType)args.iValue;
|
||||
light->UpdateLight();
|
||||
SetLightType(light->type); // for the gui changes to apply to the new type
|
||||
}
|
||||
});
|
||||
@@ -140,6 +180,10 @@ LightWindow::LightWindow(wiGUI* gui) : GUI(gui), light(nullptr)
|
||||
typeSelectorComboBox->AddItem("Directional");
|
||||
typeSelectorComboBox->AddItem("Point");
|
||||
typeSelectorComboBox->AddItem("Spot");
|
||||
typeSelectorComboBox->AddItem("Sphere");
|
||||
typeSelectorComboBox->AddItem("Disc");
|
||||
typeSelectorComboBox->AddItem("Rectangle");
|
||||
typeSelectorComboBox->AddItem("Tube");
|
||||
typeSelectorComboBox->SetTooltip("Choose the light source type...");
|
||||
lightWindow->AddWidget(typeSelectorComboBox);
|
||||
|
||||
@@ -156,6 +200,9 @@ LightWindow::~LightWindow()
|
||||
SAFE_DELETE(lightWindow);
|
||||
SAFE_DELETE(energySlider);
|
||||
SAFE_DELETE(distanceSlider);
|
||||
SAFE_DELETE(radiusSlider);
|
||||
SAFE_DELETE(widthSlider);
|
||||
SAFE_DELETE(heightSlider);
|
||||
SAFE_DELETE(fovSlider);
|
||||
SAFE_DELETE(biasSlider);
|
||||
SAFE_DELETE(shadowCheckBox);
|
||||
@@ -175,6 +222,9 @@ void LightWindow::SetLight(Light* light)
|
||||
energySlider->SetEnabled(true);
|
||||
energySlider->SetValue(light->enerDis.x);
|
||||
distanceSlider->SetValue(light->enerDis.y);
|
||||
radiusSlider->SetValue(light->radius);
|
||||
widthSlider->SetValue(light->width);
|
||||
heightSlider->SetValue(light->height);
|
||||
fovSlider->SetValue(light->enerDis.z);
|
||||
biasSlider->SetEnabled(true);
|
||||
biasSlider->SetValue(light->shadowBias);
|
||||
@@ -191,6 +241,9 @@ void LightWindow::SetLight(Light* light)
|
||||
else
|
||||
{
|
||||
distanceSlider->SetEnabled(false);
|
||||
radiusSlider->SetEnabled(false);
|
||||
widthSlider->SetEnabled(false);
|
||||
heightSlider->SetEnabled(false);
|
||||
fovSlider->SetEnabled(false);
|
||||
biasSlider->SetEnabled(false);
|
||||
shadowCheckBox->SetEnabled(false);
|
||||
@@ -210,14 +263,29 @@ void LightWindow::SetLightType(Light::LightType type)
|
||||
}
|
||||
else
|
||||
{
|
||||
distanceSlider->SetEnabled(true);
|
||||
if (type == Light::SPOT)
|
||||
if (type == Light::SPHERE || type == Light::DISC || type == Light::RECTANGLE || type == Light::TUBE)
|
||||
{
|
||||
fovSlider->SetEnabled(true);
|
||||
distanceSlider->SetEnabled(false);
|
||||
radiusSlider->SetEnabled(true);
|
||||
widthSlider->SetEnabled(true);
|
||||
heightSlider->SetEnabled(true);
|
||||
fovSlider->SetEnabled(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
fovSlider->SetEnabled(false);
|
||||
distanceSlider->SetEnabled(true);
|
||||
radiusSlider->SetEnabled(false);
|
||||
widthSlider->SetEnabled(false);
|
||||
heightSlider->SetEnabled(false);
|
||||
if (type == Light::SPOT)
|
||||
{
|
||||
fovSlider->SetEnabled(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
fovSlider->SetEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,6 +28,9 @@ public:
|
||||
wiWindow* lightWindow;
|
||||
wiSlider* energySlider;
|
||||
wiSlider* distanceSlider;
|
||||
wiSlider* radiusSlider;
|
||||
wiSlider* widthSlider;
|
||||
wiSlider* heightSlider;
|
||||
wiSlider* fovSlider;
|
||||
wiSlider* biasSlider;
|
||||
wiCheckBox* shadowCheckBox;
|
||||
|
||||
@@ -203,6 +203,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="fonts\basic.dds" />
|
||||
<Image Include="images\arealight.dds" />
|
||||
<Image Include="images\blood1.png" />
|
||||
<Image Include="images\directional_light.dds" />
|
||||
<Image Include="images\leaf.png">
|
||||
|
||||
@@ -233,6 +233,9 @@
|
||||
<Image Include="images\blood1.png">
|
||||
<Filter>images</Filter>
|
||||
</Image>
|
||||
<Image Include="images\arealight.dds">
|
||||
<Filter>images</Filter>
|
||||
</Image>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="WickedEngineEditor.rc">
|
||||
|
||||
Binary file not shown.
@@ -247,9 +247,11 @@ void main(ComputeShaderInput IN)
|
||||
}
|
||||
break;
|
||||
case 0/*DIRECTIONAL_LIGHT*/:
|
||||
case 3/*SPHERE_LIGHT*/:
|
||||
case 4/*DISC_LIGHT*/:
|
||||
case 5/*RECTANGLE_LIGHT*/:
|
||||
case 6/*TUBE_LIGHT*/:
|
||||
{
|
||||
// Directional lights always get added to our light list.
|
||||
// (Hopefully there are not too many directional lights!)
|
||||
t_AppendLight(i);
|
||||
o_AppendLight(i);
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ inline float shadowCascade(float4 shadowPos, float2 ShTex, float shadowKernel, f
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
inline LightingResult DirectionalLight(in LightArrayType light, in float3 N, in float3 V, in float3 P, in float roughness, in float3 f0)
|
||||
{
|
||||
LightingResult result;
|
||||
@@ -224,4 +225,444 @@ inline LightingResult SpotLight(in LightArrayType light, in float3 N, in float3
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// AREA LIGHTS
|
||||
|
||||
// Based on the Frostbite presentation:
|
||||
// Moving Frostbite to Physically Based Rendering by Sebastien Lagarde, Charles de Rousiers, Siggraph 2014
|
||||
// http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf
|
||||
|
||||
float cot(float x) { return cos(x) / sin(x); }
|
||||
float acot(float x) { return atan(1 / x); }
|
||||
|
||||
// Return the closest point on the line (without limit)
|
||||
float3 ClosestPointOnLine(float3 a, float3 b, float3 c)
|
||||
{
|
||||
float3 ab = b - a;
|
||||
float t = dot(c - a, ab) / dot(ab, ab);
|
||||
return a + t * ab;
|
||||
}
|
||||
// Return the closest point on the segment (with limit)
|
||||
float3 ClosestPointOnSegment(float3 a, float3 b, float3 c)
|
||||
{
|
||||
float3 ab = b - a;
|
||||
float t = dot(c - a, ab) / dot(ab, ab);
|
||||
return a + saturate(t) * ab;
|
||||
}
|
||||
float RightPyramidSolidAngle(float dist, float halfWidth, float halfHeight)
|
||||
{
|
||||
float a = halfWidth;
|
||||
float b = halfHeight;
|
||||
float h = dist;
|
||||
|
||||
return 4 * asin(a * b / sqrt((a * a + h * h) * (b * b + h * h)));
|
||||
}
|
||||
float RectangleSolidAngle(float3 worldPos,
|
||||
float3 p0, float3 p1,
|
||||
float3 p2, float3 p3)
|
||||
{
|
||||
float3 v0 = p0 - worldPos;
|
||||
float3 v1 = p1 - worldPos;
|
||||
float3 v2 = p2 - worldPos;
|
||||
float3 v3 = p3 - worldPos;
|
||||
|
||||
float3 n0 = normalize(cross(v0, v1));
|
||||
float3 n1 = normalize(cross(v1, v2));
|
||||
float3 n2 = normalize(cross(v2, v3));
|
||||
float3 n3 = normalize(cross(v3, v0));
|
||||
|
||||
|
||||
float g0 = acos(dot(-n0, n1));
|
||||
float g1 = acos(dot(-n1, n2));
|
||||
float g2 = acos(dot(-n2, n3));
|
||||
float g3 = acos(dot(-n3, n0));
|
||||
|
||||
return g0 + g1 + g2 + g3 - 2 * PI;
|
||||
}
|
||||
|
||||
|
||||
// o : ray origin
|
||||
// d : ray direction
|
||||
// center : sphere center
|
||||
// radius : sphere radius
|
||||
// returns distance on the ray to the object if hit, 0 otherwise
|
||||
float Trace_sphere(float3 o, float3 d, float3 center, float radius)
|
||||
{
|
||||
float3 rc = o - center;
|
||||
float c = dot(rc, rc) - (radius*radius);
|
||||
float b = dot(d, rc);
|
||||
float dd = b*b - c;
|
||||
float t = -b - sqrt(abs(dd));
|
||||
float st = step(0.0, min(t, dd));
|
||||
return lerp(-1.0, t, st);
|
||||
}
|
||||
// o : ray origin
|
||||
// d : ray direction
|
||||
// returns distance on the ray to the object if hit, 0 otherwise
|
||||
float Trace_plane(float3 o, float3 d, float3 planeOrigin, float3 planeNormal)
|
||||
{
|
||||
return dot(planeNormal, (planeOrigin - o) / dot(planeNormal, d));
|
||||
}
|
||||
// o : ray origin
|
||||
// d : ray direction
|
||||
// A,B,C : traingle corners
|
||||
// returns distance on the ray to the object if hit, 0 otherwise
|
||||
float Trace_triangle(float3 o, float3 d, float3 A, float3 B, float3 C)
|
||||
{
|
||||
float3 planeNormal = normalize(cross(B - A, C - B));
|
||||
float t = Trace_plane(o, d, A, planeNormal);
|
||||
float3 p = o + d*t;
|
||||
|
||||
float3 N1 = normalize(cross(B - A, p - B));
|
||||
float3 N2 = normalize(cross(C - B, p - C));
|
||||
float3 N3 = normalize(cross(A - C, p - A));
|
||||
|
||||
float d0 = dot(N1, N2);
|
||||
float d1 = dot(N2, N3);
|
||||
|
||||
float threshold = 1.0f - 0.001f;
|
||||
return (d0 > threshold && d1 > threshold) ? 1.0f : 0.0f;
|
||||
}
|
||||
// o : ray origin
|
||||
// d : ray direction
|
||||
// A,B,C,D : rectangle corners
|
||||
// returns distance on the ray to the object if hit, 0 otherwise
|
||||
float Trace_rectangle(float3 o, float3 d, float3 A, float3 B, float3 C, float3 D)
|
||||
{
|
||||
return max(Trace_triangle(o, d, A, B, C), Trace_triangle(o, d, C, D, A));
|
||||
}
|
||||
// o : ray origin
|
||||
// d : ray direction
|
||||
// diskNormal : disk facing direction
|
||||
// returns distance on the ray to the object if hit, 0 otherwise
|
||||
float Trace_disk(float3 o, float3 d, float3 diskCenter, float diskRadius, float3 diskNormal)
|
||||
{
|
||||
float t = Trace_plane(o, d, diskCenter, diskNormal);
|
||||
float3 p = o + d*t;
|
||||
float3 diff = p - diskCenter;
|
||||
return dot(diff, diff)<sqr(diskRadius);
|
||||
}
|
||||
|
||||
inline float3 getDiffuseDominantDir(float3 N, float3 V, float NdotV, float roughness)
|
||||
{
|
||||
float a = 1.02341f * roughness - 1.51174f;
|
||||
float b = -0.511705f * roughness + 0.755868f;
|
||||
float lerpFactor = saturate((NdotV * a + b) * roughness);
|
||||
|
||||
return normalize(lerp(N, V, lerpFactor));
|
||||
}
|
||||
inline float3 getSpecularDominantDirArea(float3 N, float3 R, float roughness)
|
||||
{
|
||||
// Simple linear approximation
|
||||
float lerpFactor = (1 - roughness);
|
||||
|
||||
return normalize(lerp(N, R, lerpFactor));
|
||||
}
|
||||
|
||||
float illuminanceSphereOrDisk(float cosTheta, float sinSigmaSqr)
|
||||
{
|
||||
float sinTheta = sqrt(1.0f - cosTheta * cosTheta);
|
||||
|
||||
float illuminance = 0.0f;
|
||||
// Note: Following test is equivalent to the original formula.
|
||||
// There is 3 phase in the curve: cosTheta > sqrt(sinSigmaSqr),
|
||||
// cosTheta > -sqrt(sinSigmaSqr) and else it is 0
|
||||
// The two outer case can be merge into a cosTheta * cosTheta > sinSigmaSqr
|
||||
// and using saturate(cosTheta) instead.
|
||||
if (cosTheta * cosTheta > sinSigmaSqr)
|
||||
{
|
||||
illuminance = PI * sinSigmaSqr * saturate(cosTheta);
|
||||
}
|
||||
else
|
||||
{
|
||||
float x = sqrt(1.0f / sinSigmaSqr - 1.0f); // For a disk this simplify to x = d / r
|
||||
float y = -x * (cosTheta / sinTheta);
|
||||
float sinThetaSqrtY = sinTheta * sqrt(1.0f - y * y);
|
||||
illuminance = (cosTheta * acos(y) - x * sinThetaSqrtY) * sinSigmaSqr + atan(sinThetaSqrtY / x);
|
||||
}
|
||||
|
||||
return max(illuminance, 0.0f);
|
||||
}
|
||||
|
||||
inline float3 _GetLeft(LightArrayType light) { return light.directionWS; }
|
||||
inline float3 _GetUp(LightArrayType light) { return light.directionVS; }
|
||||
inline float3 _GetFront(LightArrayType light) { return light.positionVS; }
|
||||
inline float _GetRadius(LightArrayType light) { return light.texMulAdd.x; }
|
||||
inline float _GetWidth(LightArrayType light) { return light.texMulAdd.y; }
|
||||
inline float _GetHeight(LightArrayType light) { return light.texMulAdd.z; }
|
||||
|
||||
inline LightingResult SphereLight(in LightArrayType light, in float3 N, in float3 V, in float3 P, in float roughness, in float3 f0)
|
||||
{
|
||||
LightingResult result = (LightingResult)0;
|
||||
|
||||
float3 Lunormalized = light.positionWS - P;
|
||||
float dist = length(Lunormalized);
|
||||
float3 L = Lunormalized / dist;
|
||||
|
||||
float sqrDist = dot(Lunormalized, Lunormalized);
|
||||
|
||||
float cosTheta = clamp(dot(N, L), -0.999, 0.999); // Clamp to avoid edge case
|
||||
// We need to prevent the object penetrating into the surface
|
||||
// and we must avoid divide by 0, thus the 0.9999f
|
||||
float sqrLightRadius = _GetRadius(light) * _GetRadius(light);
|
||||
float sinSigmaSqr = min(sqrLightRadius / sqrDist, 0.9999f);
|
||||
float fLight = illuminanceSphereOrDisk(cosTheta, sinSigmaSqr);
|
||||
|
||||
|
||||
// We approximate L by the closest point on the reflection ray to the light source (representative point technique) to achieve a nice looking specular reflection
|
||||
{
|
||||
float3 r = reflect(V, N);
|
||||
r = getSpecularDominantDirArea(N, r, roughness);
|
||||
|
||||
float3 centerToRay = dot(Lunormalized, r) * r - Lunormalized;
|
||||
float3 closestPoint = Lunormalized + centerToRay * saturate(_GetRadius(light) / length(centerToRay));
|
||||
L = normalize(closestPoint);
|
||||
}
|
||||
|
||||
float3 lightColor = light.color.rgb*light.energy;
|
||||
|
||||
BRDF_MAKE(N, L, V);
|
||||
result.specular = lightColor * BRDF_SPECULAR(roughness, f0) * fLight;
|
||||
result.diffuse = lightColor * fLight / PI;
|
||||
|
||||
result.diffuse = max(0.0f, result.diffuse);
|
||||
result.specular = max(0.0f, result.specular);
|
||||
|
||||
return result;
|
||||
}
|
||||
inline LightingResult DiscLight(in LightArrayType light, in float3 N, in float3 V, in float3 P, in float roughness, in float3 f0)
|
||||
{
|
||||
LightingResult result = (LightingResult)0;
|
||||
|
||||
float3 Lunormalized = light.positionWS - P;
|
||||
float dist = length(Lunormalized);
|
||||
float3 L = Lunormalized / dist;
|
||||
|
||||
float sqrDist = dot(Lunormalized, Lunormalized);
|
||||
|
||||
float3 lightPlaneNormal = _GetFront(light);
|
||||
|
||||
float cosTheta = clamp(dot(N, L), -0.999, 0.999);
|
||||
float sqrLightRadius = _GetRadius(light) * _GetRadius(light);
|
||||
// Do not let the surface penetrate the light
|
||||
float sinSigmaSqr = sqrLightRadius / (sqrLightRadius + max(sqrLightRadius, sqrDist));
|
||||
// Multiply by saturate(dot(planeNormal , -L)) to better match ground truth.
|
||||
float fLight = illuminanceSphereOrDisk(cosTheta, sinSigmaSqr)
|
||||
* saturate(dot(lightPlaneNormal, -L));
|
||||
|
||||
// We approximate L by the closest point on the reflection ray to the light source (representative point technique) to achieve a nice looking specular reflection
|
||||
{
|
||||
float3 r = reflect(V, N);
|
||||
r = getSpecularDominantDirArea(N, r, roughness);
|
||||
|
||||
float t = Trace_plane(P, r, light.positionWS, lightPlaneNormal);
|
||||
float3 p = P + r*t;
|
||||
float3 centerToRay = p - light.positionWS;
|
||||
float3 closestPoint = Lunormalized + centerToRay * saturate(_GetRadius(light) / length(centerToRay));
|
||||
L = normalize(closestPoint);
|
||||
}
|
||||
|
||||
float3 lightColor = light.color.rgb*light.energy;
|
||||
|
||||
BRDF_MAKE(N, L, V);
|
||||
result.specular = lightColor * BRDF_SPECULAR(roughness, f0) * fLight;
|
||||
result.diffuse = lightColor * fLight / PI;
|
||||
|
||||
result.diffuse = max(0.0f, result.diffuse);
|
||||
result.specular = max(0.0f, result.specular);
|
||||
|
||||
return result;
|
||||
}
|
||||
inline LightingResult RectangleLight(in LightArrayType light, in float3 N, in float3 V, in float3 P, in float roughness, in float3 f0)
|
||||
{
|
||||
LightingResult result = (LightingResult)0;
|
||||
|
||||
|
||||
float3 L = light.positionWS - P;
|
||||
float dist = length(L);
|
||||
L /= dist;
|
||||
|
||||
|
||||
float3 lightPlaneNormal = _GetFront(light);
|
||||
float3 lightLeft = _GetLeft(light);
|
||||
float3 lightUp = _GetUp(light);
|
||||
float lightWidth = _GetWidth(light);
|
||||
float lightHeight = _GetHeight(light);
|
||||
float3 worldPos = P;
|
||||
float3 worldNormal = N;
|
||||
|
||||
|
||||
float fLight = 0;
|
||||
float halfWidth = lightWidth * 0.5;
|
||||
float halfHeight = lightHeight * 0.5;
|
||||
float3 p0 = light.positionWS + lightLeft * -halfWidth + lightUp * halfHeight;
|
||||
float3 p1 = light.positionWS + lightLeft * -halfWidth + lightUp * -halfHeight;
|
||||
|
||||
float3 p2 = light.positionWS + lightLeft * halfWidth + lightUp * -halfHeight;
|
||||
float3 p3 = light.positionWS + lightLeft * halfWidth + lightUp * halfHeight;
|
||||
float solidAngle = RectangleSolidAngle(worldPos, p0, p1, p2, p3);
|
||||
|
||||
if (dot(worldPos - light.positionWS, lightPlaneNormal) > 0)
|
||||
{
|
||||
fLight = solidAngle * 0.2 * (
|
||||
saturate(dot(normalize(p0 - worldPos), worldNormal)) +
|
||||
saturate(dot(normalize(p1 - worldPos), worldNormal)) +
|
||||
saturate(dot(normalize(p2 - worldPos), worldNormal)) +
|
||||
saturate(dot(normalize(p3 - worldPos), worldNormal)) +
|
||||
saturate(dot(normalize(light.positionWS - worldPos), worldNormal)));
|
||||
}
|
||||
fLight = max(0, fLight);
|
||||
|
||||
|
||||
// We approximate L by the closest point on the reflection ray to the light source (representative point technique) to achieve a nice looking specular reflection
|
||||
{
|
||||
float3 r = reflect(-V, N);
|
||||
r = getSpecularDominantDirArea(N, r, roughness);
|
||||
|
||||
float traced = Trace_rectangle(P, r, p0, p1, p2, p3);
|
||||
|
||||
[branch]
|
||||
if (traced > 0)
|
||||
{
|
||||
// Trace succeeded so the light vector L is the reflection vector itself
|
||||
L = r;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The trace didn't succeed, so we need to find the closest point to the ray on the rectangle
|
||||
|
||||
// We find the intersection point on the plane of the rectangle
|
||||
float3 tracedPlane = P + r * Trace_plane(P, r, light.positionWS, lightPlaneNormal);
|
||||
|
||||
// Then find the closest point along the edges of the rectangle (edge = segment)
|
||||
float3 PC[4] = {
|
||||
ClosestPointOnSegment(p0, p1, tracedPlane),
|
||||
ClosestPointOnSegment(p1, p2, tracedPlane),
|
||||
ClosestPointOnSegment(p2, p3, tracedPlane),
|
||||
ClosestPointOnSegment(p3, p0, tracedPlane),
|
||||
};
|
||||
float dist[4] = {
|
||||
distance(PC[0], tracedPlane),
|
||||
distance(PC[1], tracedPlane),
|
||||
distance(PC[2], tracedPlane),
|
||||
distance(PC[3], tracedPlane),
|
||||
};
|
||||
|
||||
float3 min = PC[0];
|
||||
float minDist = dist[0];
|
||||
[unroll]
|
||||
for (uint iLoop = 1; iLoop < 4; iLoop++)
|
||||
{
|
||||
if (dist[iLoop] < minDist)
|
||||
{
|
||||
minDist = dist[iLoop];
|
||||
min = PC[iLoop];
|
||||
}
|
||||
}
|
||||
|
||||
L = min - P;
|
||||
}
|
||||
L = normalize(L); // TODO: Is it necessary?
|
||||
}
|
||||
|
||||
float3 lightColor = light.color.rgb*light.energy;
|
||||
|
||||
BRDF_MAKE(N, L, V);
|
||||
result.specular = lightColor * BRDF_SPECULAR(roughness, f0) * fLight;
|
||||
result.diffuse = lightColor * fLight / PI;
|
||||
|
||||
result.diffuse = max(0.0f, result.diffuse);
|
||||
result.specular = max(0.0f, result.specular);
|
||||
|
||||
return result;
|
||||
}
|
||||
inline LightingResult TubeLight(in LightArrayType light, in float3 N, in float3 V, in float3 P, in float roughness, in float3 f0)
|
||||
{
|
||||
LightingResult result = (LightingResult)0;
|
||||
|
||||
float3 Lunormalized = light.positionWS - P;
|
||||
float dist = length(Lunormalized);
|
||||
float3 L = Lunormalized / dist;
|
||||
|
||||
float sqrDist = dot(Lunormalized, Lunormalized);
|
||||
|
||||
float3 lightLeft = _GetLeft(light);
|
||||
float lightWidth = _GetWidth(light);
|
||||
float3 worldPos = P;
|
||||
float3 worldNormal = N;
|
||||
|
||||
|
||||
float3 P0 = light.positionWS - lightLeft*lightWidth*0.5f;
|
||||
float3 P1 = light.positionWS + lightLeft*lightWidth*0.5f;
|
||||
|
||||
// The sphere is placed at the nearest point on the segment.
|
||||
// The rectangular plane is define by the following orthonormal frame:
|
||||
float3 forward = normalize(ClosestPointOnLine(P0, P1, worldPos) - worldPos);
|
||||
float3 left = lightLeft;
|
||||
float3 up = cross(lightLeft, forward);
|
||||
|
||||
float3 p0 = light.positionWS - left * (0.5 * lightWidth) + _GetRadius(light) * up;
|
||||
float3 p1 = light.positionWS - left * (0.5 * lightWidth) - _GetRadius(light) * up;
|
||||
float3 p2 = light.positionWS + left * (0.5 * lightWidth) - _GetRadius(light) * up;
|
||||
float3 p3 = light.positionWS + left * (0.5 * lightWidth) + _GetRadius(light) * up;
|
||||
|
||||
|
||||
float solidAngle = RectangleSolidAngle(worldPos, p0, p1, p2, p3);
|
||||
|
||||
float fLight = solidAngle * 0.2 * (
|
||||
saturate(dot(normalize(p0 - worldPos), worldNormal)) +
|
||||
saturate(dot(normalize(p1 - worldPos), worldNormal)) +
|
||||
saturate(dot(normalize(p2 - worldPos), worldNormal)) +
|
||||
saturate(dot(normalize(p3 - worldPos), worldNormal)) +
|
||||
saturate(dot(normalize(light.positionWS - worldPos), worldNormal)));
|
||||
|
||||
// We then add the contribution of the sphere
|
||||
float3 spherePosition = ClosestPointOnSegment(P0, P1, worldPos);
|
||||
float3 sphereUnormL = spherePosition - worldPos;
|
||||
float3 sphereL = normalize(sphereUnormL);
|
||||
float sqrSphereDistance = dot(sphereUnormL, sphereUnormL);
|
||||
|
||||
float fLightSphere = PI * saturate(dot(sphereL, worldNormal)) *
|
||||
((_GetRadius(light) * _GetRadius(light)) / sqrSphereDistance);
|
||||
|
||||
fLight += fLightSphere;
|
||||
|
||||
fLight = max(0, fLight);
|
||||
|
||||
|
||||
// We approximate L by the closest point on the reflection ray to the light source (representative point technique) to achieve a nice looking specular reflection
|
||||
{
|
||||
float3 r = reflect(V, N);
|
||||
r = getSpecularDominantDirArea(N, r, roughness);
|
||||
|
||||
// First, the closest point to the ray on the segment
|
||||
float3 L0 = P0 - P;
|
||||
float3 L1 = P1 - P;
|
||||
float3 Ld = L1 - L0;
|
||||
|
||||
float t = dot(r, L0) * dot(r, Ld) - dot(L0, Ld);
|
||||
t /= dot(Ld, Ld) - sqr(dot(r, Ld));
|
||||
|
||||
L = (L0 + saturate(t) * Ld);
|
||||
|
||||
// Then I place a sphere on that point and calculate the lisght vector like for sphere light.
|
||||
float3 centerToRay = dot(L, r) * r - L;
|
||||
float3 closestPoint = L + centerToRay * saturate(_GetRadius(light) / length(centerToRay));
|
||||
L = normalize(closestPoint);
|
||||
}
|
||||
|
||||
float3 lightColor = light.color.rgb*light.energy;
|
||||
|
||||
BRDF_MAKE(N, L, V);
|
||||
result.specular = lightColor * BRDF_SPECULAR(roughness, f0) * fLight;
|
||||
result.diffuse = lightColor * fLight / PI;
|
||||
|
||||
result.diffuse = max(0.0f, result.diffuse);
|
||||
result.specular = max(0.0f, result.specular);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // _LIGHTING_HF_
|
||||
@@ -154,6 +154,26 @@ inline void TiledLighting(in float2 pixel, in float3 N, in float3 V, in float3 P
|
||||
result = SpotLight(light, N, V, P, roughness, f0);
|
||||
}
|
||||
break;
|
||||
case 3/*SPHERE*/:
|
||||
{
|
||||
result = SphereLight(light, N, V, P, roughness, f0);
|
||||
}
|
||||
break;
|
||||
case 4/*DISC*/:
|
||||
{
|
||||
result = DiscLight(light, N, V, P, roughness, f0);
|
||||
}
|
||||
break;
|
||||
case 5/*RECTANGLE*/:
|
||||
{
|
||||
result = RectangleLight(light, N, V, P, roughness, f0);
|
||||
}
|
||||
break;
|
||||
case 6/*TUBE*/:
|
||||
{
|
||||
result = TubeLight(light, N, V, P, roughness, f0);
|
||||
}
|
||||
break;
|
||||
#ifndef DISABLE_DECALS
|
||||
case 100/*DECAL*/:
|
||||
{
|
||||
@@ -186,7 +206,7 @@ inline void TiledLighting(in float2 pixel, in float3 N, in float3 V, in float3 P
|
||||
////////////
|
||||
|
||||
#define OBJECT_PS_MAKE_COMMON \
|
||||
float3 N = input.nor; \
|
||||
float3 N = normalize(input.nor); \
|
||||
float3 P = input.pos3D; \
|
||||
float3 V = g_xCamera_CamPos - P; \
|
||||
float dist = length(V); \
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "wiHelper.h"
|
||||
|
||||
// this should always be only INCREMENTED and only if a new serialization is implemeted somewhere!
|
||||
uint64_t __archiveVersion = 5;
|
||||
uint64_t __archiveVersion = 6;
|
||||
// this is the version number of which below the archive is not compatible with the current version
|
||||
uint64_t __archiveVersionBarrier = 1;
|
||||
|
||||
|
||||
@@ -3926,6 +3926,9 @@ Light::Light():Transform() {
|
||||
shadowMap_index = -1;
|
||||
lightArray_index = 0;
|
||||
shadowBias = 0.0001f;
|
||||
radius = 1.0f;
|
||||
width = 1.0f;
|
||||
height = 1.0f;
|
||||
}
|
||||
Light::~Light() {
|
||||
for (string x : lensFlareNames)
|
||||
@@ -4068,6 +4071,13 @@ void Light::Serialize(wiArchive& archive)
|
||||
lensFlareNames.push_back(rim);
|
||||
}
|
||||
}
|
||||
|
||||
if (archive.GetVersion() >= 6)
|
||||
{
|
||||
archive >> radius;
|
||||
archive >> width;
|
||||
archive >> height;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -4082,6 +4092,13 @@ void Light::Serialize(wiArchive& archive)
|
||||
{
|
||||
archive << wiHelper::GetFileNameFromPath(x);
|
||||
}
|
||||
|
||||
if (archive.GetVersion() >= 6)
|
||||
{
|
||||
archive << radius;
|
||||
archive << width;
|
||||
archive << height;
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
@@ -749,10 +749,17 @@ struct Light : public Cullable , public Transform
|
||||
|
||||
float shadowBias;
|
||||
|
||||
// area light props:
|
||||
float radius, width, height;
|
||||
|
||||
enum LightType{
|
||||
DIRECTIONAL,
|
||||
POINT,
|
||||
SPOT,
|
||||
SPHERE,
|
||||
DISC,
|
||||
RECTANGLE,
|
||||
TUBE,
|
||||
LIGHTTYPE_COUNT,
|
||||
};
|
||||
LightType type;
|
||||
|
||||
@@ -1715,6 +1715,18 @@ void wiRenderer::UpdateRenderData(GRAPHICSTHREAD threadID)
|
||||
lightArray[lightCounter].shadowKernel = 1.0f / SHADOWRES_CUBE;
|
||||
}
|
||||
break;
|
||||
case Light::SPHERE:
|
||||
case Light::DISC:
|
||||
case Light::RECTANGLE:
|
||||
case Light::TUBE:
|
||||
{
|
||||
XMMATRIX lightMat = XMLoadFloat4x4(&l->world);
|
||||
XMStoreFloat3(&lightArray[lightCounter].directionWS, XMVector3TransformNormal(XMVectorSet(-1, 0, 0, 0), lightMat)); // left dir
|
||||
XMStoreFloat3(&lightArray[lightCounter].directionVS, XMVector3TransformNormal(XMVectorSet(0, 1, 0, 0), lightMat)); // up dir
|
||||
XMStoreFloat3(&lightArray[lightCounter].posVS, XMVector3TransformNormal(XMVectorSet(0, 0, -1, 0), lightMat)); // front dir
|
||||
lightArray[lightCounter].texMulAdd = XMFLOAT4(l->radius, l->width, l->height, 0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace wiVersion
|
||||
// minor features, major updates
|
||||
const int minor = 9;
|
||||
// minor bug fixes, alterations, refactors, updates
|
||||
const int revision = 53;
|
||||
const int revision = 54;
|
||||
|
||||
|
||||
long GetVersion()
|
||||
|
||||
Reference in New Issue
Block a user