void Application_Idle(object sender, EventArgs e) { if (m_device == null) { return; } // Setup global data m_CB_Main.m.iResolution = new float2(panelOutput.Width, panelOutput.Height); m_CB_Main.m.tanHalfFOV = Mathf.Tan(0.5f * Mathf.ToRad(FOV_DEGREES)); m_CB_Main.m.iGlobalTime = GetGameTime() - m_startTime; m_CB_Main.UpdateData(); // Setup light data float3 wsLightPosition = new float3(floatTrackbarControlLightPosX.Value, floatTrackbarControlLightPosY.Value, floatTrackbarControlLightPosZ.Value); float3 at = (m_wsLightTargetPosition - wsLightPosition).Normalized; if (radioButtonNegativeFreeTarget.Checked) { at = -at; } else if (radioButtonHorizontalTarget.Checked) { at.y = 0; at.Normalize(); } float roll = Mathf.ToRad(floatTrackbarControlLightRoll.Value); float3 left, up; at.OrthogonalBasis(out left, out up); float3 axisX = Mathf.Cos(roll) * left + Mathf.Sin(roll) * up; float3 axisY = -Mathf.Sin(roll) * left + Mathf.Cos(roll) * up; float radiusX = floatTrackbarControlLightScaleX.Value; float radiusY = floatTrackbarControlLightScaleY.Value; m_CB_Light.m._luminance = floatTrackbarControlLuminance.Value; m_CB_Light.m._wsLight2World.r0.Set(axisX, radiusX); m_CB_Light.m._wsLight2World.r1.Set(axisY, radiusY); m_CB_Light.m._wsLight2World.r2.Set(at, Mathf.PI * radiusX * radiusY); // Disk area in W m_CB_Light.m._wsLight2World.r3.Set(wsLightPosition, 1); m_CB_Light.UpdateData(); // Upload FGD & LTC tables m_tex_MSBRDF_E.SetPS(2); m_tex_MSBRDF_Eavg.SetPS(3); m_tex_LTC.SetPS(4); m_tex_MS_LTC.SetPS(5); // =========== Render scene =========== m_device.SetRenderStates(RASTERIZER_STATE.CULL_NONE, DEPTHSTENCIL_STATE.READ_WRITE_DEPTH_LESS, BLEND_STATE.DISABLED); if (!checkBoxShowDiff.Checked) { m_device.SetRenderTarget(m_device.DefaultTarget, m_device.DefaultDepthStencil); m_device.Clear(m_device.DefaultTarget, float4.Zero); m_device.ClearDepthStencil(m_device.DefaultDepthStencil, 1.0f, 0, true, false); if (checkBoxShowReference.Checked) { // Use expensive reference if (m_shader_RenderScene_Reference.Use()) { m_device.RenderFullscreenQuad(m_shader_RenderScene_Reference); } } else { if (m_shader_RenderScene.Use()) { m_device.RenderFullscreenQuad(m_shader_RenderScene); } } } else { // Render reference in RT0 m_device.SetRenderTarget(m_RT_temp0, m_device.DefaultDepthStencil); m_device.ClearDepthStencil(m_device.DefaultDepthStencil, 1.0f, 0, true, false); if (m_shader_RenderScene_Reference.Use()) { m_device.RenderFullscreenQuad(m_shader_RenderScene_Reference); } // Render LTC in RT1 m_device.SetRenderTarget(m_RT_temp1, m_device.DefaultDepthStencil); m_device.ClearDepthStencil(m_device.DefaultDepthStencil, 1.0f, 0, true, false); if (m_shader_RenderScene.Use()) { m_device.RenderFullscreenQuad(m_shader_RenderScene); } // Render difference m_device.SetRenderStates(RASTERIZER_STATE.NOCHANGE, DEPTHSTENCIL_STATE.DISABLED, BLEND_STATE.DISABLED); m_device.SetRenderTarget(m_device.DefaultTarget, null); if (m_shader_RenderDiff.Use()) { m_RT_temp0.SetPS(0); m_RT_temp1.SetPS(1); m_tex_FalseColors.SetPS(2); m_device.RenderFullscreenQuad(m_shader_RenderDiff); } } // =========== Render Light Disk =========== m_device.SetRenderTarget(m_device.DefaultTarget, m_device.DefaultDepthStencil); if (!checkBoxDebugMatrix.Checked) { m_device.SetRenderStates(RASTERIZER_STATE.CULL_NONE, DEPTHSTENCIL_STATE.READ_WRITE_DEPTH_LESS, BLEND_STATE.DISABLED); if (m_shader_RenderLight.Use()) { m_prim_disk.Render(m_shader_RenderLight); } } else { if (m_shader_RenderTestQuad.Use()) { m_CB_TestQuad.m._wsLight2World.r0.Set(radiusX * axisX, 0); m_CB_TestQuad.m._wsLight2World.r1.Set(radiusY * axisY, 0); m_CB_TestQuad.m._wsLight2World.r2.Set(at, 0); m_CB_TestQuad.m._wsLight2World.r3.Set(wsLightPosition, 1); // Upload the full matrix, although we only really need the 4 non trivial coefficients at indices m11, m13, m31 and m33... int roughnessIndex = (int)Mathf.Floor(63.99f * Mathf.Sqrt(floatTrackbarControlRoughness.Value)); int thetaIndex = (int)Mathf.Floor(63.99f * Mathf.Sqrt(1.0f - Mathf.Cos(Mathf.ToRad(floatTrackbarControlViewAngle.Value)))); int matrixIndex = roughnessIndex + 64 * thetaIndex; double[,] LTC = radioButtonGGX.Checked ? LTCAreaLight.s_LtcMatrixData_GGX : LTCAreaLight.s_LtcMatrixData_OrenNayar; // NOTE: The LTC inverse matrices stored in the tables are transposed: columns are stored first // So in order to use them in the shaders, we need to compute P * M^-1^T instead of the paper's formulation M^-1 * P // m_CB_TestQuad.m._invM_transposed_r0.Set((float)LTC[matrixIndex, 0], (float)LTC[matrixIndex, 1], (float)LTC[matrixIndex, 2], 0); m_CB_TestQuad.m._invM_transposed_r1.Set((float)LTC[matrixIndex, 3], (float)LTC[matrixIndex, 4], (float)LTC[matrixIndex, 5], 0); m_CB_TestQuad.m._invM_transposed_r2.Set((float)LTC[matrixIndex, 6], (float)LTC[matrixIndex, 7], (float)LTC[matrixIndex, 8], 0); m_CB_TestQuad.UpdateData(); m_device.RenderFullscreenQuad(m_shader_RenderTestQuad); } } // Show! m_device.Present(false); }