public override void Render(IList <SceneNode> nodes, RenderContext context, RenderOrder order) { if (nodes == null) { throw new ArgumentNullException("nodes"); } if (context == null) { throw new ArgumentNullException("context"); } int numberOfNodes = nodes.Count; if (numberOfNodes == 0) { return; } context.Validate(_effect); context.ThrowIfCameraMissing(); var graphicsDevice = _effect.GraphicsDevice; var savedRenderState = new RenderStateSnapshot(graphicsDevice); graphicsDevice.DepthStencilState = DepthStencilState.None; graphicsDevice.RasterizerState = RasterizerState.CullNone; graphicsDevice.BlendState = GraphicsHelper.BlendStateAdd; var viewport = graphicsDevice.Viewport; _parameterViewportSize.SetValue(new Vector2(viewport.Width, viewport.Height)); _parameterGBuffer0.SetValue(context.GBuffer0); _parameterGBuffer1.SetValue(context.GBuffer1); var cameraNode = context.CameraNode; Pose cameraPose = cameraNode.PoseWorld; Matrix viewProjection = (Matrix)cameraNode.View * cameraNode.Camera.Projection; // Update SceneNode.LastFrame for all visible nodes. int frame = context.Frame; context.CameraNode.LastFrame = frame; var isHdrEnabled = context.IsHdrEnabled(); for (int i = 0; i < numberOfNodes; i++) { var lightNode = nodes[i] as LightNode; if (lightNode == null) { continue; } var light = lightNode.Light as PointLight; if (light == null) { continue; } // LightNode is visible in current frame. lightNode.LastFrame = frame; float hdrScale = isHdrEnabled ? light.HdrScale : 1; _parameterDiffuseColor.SetValue((Vector3)light.Color * light.DiffuseIntensity * hdrScale); _parameterSpecularColor.SetValue((Vector3)light.Color * light.SpecularIntensity * hdrScale); Pose lightPose = lightNode.PoseWorld; bool hasShadow = (lightNode.Shadow != null && lightNode.Shadow.ShadowMask != null); if (hasShadow) { switch (lightNode.Shadow.ShadowMaskChannel) { case 0: _parameterShadowMaskChannel.SetValue(new Vector4(1, 0, 0, 0)); break; case 1: _parameterShadowMaskChannel.SetValue(new Vector4(0, 1, 0, 0)); break; case 2: _parameterShadowMaskChannel.SetValue(new Vector4(0, 0, 1, 0)); break; default: _parameterShadowMaskChannel.SetValue(new Vector4(0, 0, 0, 1)); break; } _parameterShadowMask.SetValue(lightNode.Shadow.ShadowMask); } _parameterPosition.SetValue((Vector3)(lightPose.Position - cameraPose.Position)); _parameterRange.SetValue(light.Range); _parameterAttenuation.SetValue(light.Attenuation); bool hasTexture = (light.Texture != null); if (hasTexture) { _parameterTexture.SetValue(light.Texture); // Cube maps are left handed --> Sample with inverted z. (Otherwise, the // cube map and objects or texts in it are mirrored.) var mirrorZ = Matrix.CreateScale(1, 1, -1); _parameterTextureMatrix.SetValue((Matrix)(mirrorZ * lightPose.Inverse)); } var rectangle = GraphicsHelper.GetViewportRectangle(cameraNode, viewport, lightPose.Position, light.Range); var texCoordTopLeft = new Vector2F(rectangle.Left / (float)viewport.Width, rectangle.Top / (float)viewport.Height); var texCoordBottomRight = new Vector2F(rectangle.Right / (float)viewport.Width, rectangle.Bottom / (float)viewport.Height); GraphicsHelper.GetFrustumFarCorners(cameraNode.Camera.Projection, texCoordTopLeft, texCoordBottomRight, _frustumFarCorners); // Convert frustum far corners from view space to world space. for (int j = 0; j < _frustumFarCorners.Length; j++) { _frustumFarCorners[j] = (Vector3)cameraPose.ToWorldDirection((Vector3)_frustumFarCorners[j]); } _parameterFrustumCorners.SetValue(_frustumFarCorners); if (lightNode.Clip != null) { var data = lightNode.RenderData as LightRenderData; if (data == null) { data = new LightRenderData(); lightNode.RenderData = data; } data.UpdateClipSubmesh(context.GraphicsService, lightNode); graphicsDevice.DepthStencilState = GraphicsHelper.DepthStencilStateOnePassStencilFail; graphicsDevice.BlendState = GraphicsHelper.BlendStateNoColorWrite; _parameterWorldViewProjection.SetValue((Matrix)data.ClipMatrix * viewProjection); _passClip.Apply(); data.ClipSubmesh.Draw(); graphicsDevice.DepthStencilState = lightNode.InvertClip ? GraphicsHelper.DepthStencilStateStencilEqual0 : GraphicsHelper.DepthStencilStateStencilNotEqual0; graphicsDevice.BlendState = GraphicsHelper.BlendStateAdd; } else { graphicsDevice.DepthStencilState = DepthStencilState.None; } if (hasShadow) { if (hasTexture) { if (light.Texture.Format == SurfaceFormat.Alpha8) { _passShadowedTexturedAlpha.Apply(); } else { _passShadowedTexturedRgb.Apply(); } } else { _passShadowed.Apply(); } } else { if (hasTexture) { if (light.Texture.Format == SurfaceFormat.Alpha8) { _passTexturedAlpha.Apply(); } else { _passTexturedRgb.Apply(); } } else { _passDefault.Apply(); } } graphicsDevice.DrawQuad(rectangle); } savedRenderState.Restore(); }
public override void Render(IList <SceneNode> nodes, RenderContext context, RenderOrder order) { if (nodes == null) { throw new ArgumentNullException("nodes"); } if (context == null) { throw new ArgumentNullException("context"); } int numberOfNodes = nodes.Count; if (numberOfNodes == 0) { return; } context.Validate(_effect); context.ThrowIfCameraMissing(); var graphicsDevice = _effect.GraphicsDevice; var savedRenderState = new RenderStateSnapshot(graphicsDevice); graphicsDevice.DepthStencilState = DepthStencilState.None; graphicsDevice.RasterizerState = RasterizerState.CullNone; graphicsDevice.BlendState = GraphicsHelper.BlendStateAdd; var viewport = graphicsDevice.Viewport; _parameterViewportSize.SetValue(new Vector2(viewport.Width, viewport.Height)); _parameterGBuffer0.SetValue(context.GBuffer0); _parameterGBuffer1.SetValue(context.GBuffer1); var cameraNode = context.CameraNode; Matrix viewProjection = (Matrix)cameraNode.View * cameraNode.Camera.Projection; var cameraPose = cameraNode.PoseWorld; GraphicsHelper.GetFrustumFarCorners(cameraNode.Camera.Projection, _cameraFrustumFarCorners); // Convert frustum far corners from view space to world space. for (int i = 0; i < _cameraFrustumFarCorners.Length; i++) { _cameraFrustumFarCorners[i] = (Vector3)cameraPose.ToWorldDirection((Vector3)_cameraFrustumFarCorners[i]); } _parameterFrustumCorners.SetValue(_cameraFrustumFarCorners); // Update SceneNode.LastFrame for all visible nodes. int frame = context.Frame; cameraNode.LastFrame = frame; var isHdrEnabled = context.IsHdrEnabled(); for (int i = 0; i < numberOfNodes; i++) { var lightNode = nodes[i] as LightNode; if (lightNode == null) { continue; } var light = lightNode.Light as DirectionalLight; if (light == null) { continue; } // LightNode is visible in current frame. lightNode.LastFrame = frame; float hdrScale = isHdrEnabled ? light.HdrScale : 1; _parameterDiffuseColor.SetValue((Vector3)light.Color * light.DiffuseIntensity * hdrScale); _parameterSpecularColor.SetValue((Vector3)light.Color * light.SpecularIntensity * hdrScale); Pose lightPose = lightNode.PoseWorld; Vector3 lightDirectionWorld = lightPose.ToWorldDirection(Vector3.Forward); _parameterLightDirection.SetValue((Vector3)lightDirectionWorld); bool hasShadow = (lightNode.Shadow != null && lightNode.Shadow.ShadowMask != null); if (hasShadow) { switch (lightNode.Shadow.ShadowMaskChannel) { case 0: _parameterShadowMaskChannel.SetValue(new Vector4(1, 0, 0, 0)); break; case 1: _parameterShadowMaskChannel.SetValue(new Vector4(0, 1, 0, 0)); break; case 2: _parameterShadowMaskChannel.SetValue(new Vector4(0, 0, 1, 0)); break; default: _parameterShadowMaskChannel.SetValue(new Vector4(0, 0, 0, 1)); break; } _parameterShadowMask.SetValue(lightNode.Shadow.ShadowMask); } bool hasTexture = (light.Texture != null); if (hasTexture) { var textureProjection = Matrix.CreateOrthographicOffCenter( -light.TextureOffset.X, -light.TextureOffset.X + Math.Abs(light.TextureScale.X), light.TextureOffset.Y, light.TextureOffset.Y + Math.Abs(light.TextureScale.Y), 1, // Not relevant 2); // Not relevant. var scale = Matrix.CreateScale(Math.Sign(light.TextureScale.X), Math.Sign(light.TextureScale.Y), 1); _parameterTextureMatrix.SetValue((Matrix)(GraphicsHelper.ProjectorBiasMatrix * scale * textureProjection * lightPose.Inverse)); _parameterTexture.SetValue(light.Texture); } if (lightNode.Clip != null) { var data = lightNode.RenderData as LightRenderData; if (data == null) { data = new LightRenderData(); lightNode.RenderData = data; } data.UpdateClipSubmesh(context.GraphicsService, lightNode); graphicsDevice.DepthStencilState = GraphicsHelper.DepthStencilStateOnePassStencilFail; graphicsDevice.BlendState = GraphicsHelper.BlendStateNoColorWrite; _parameterWorldViewProjection.SetValue((Matrix)data.ClipMatrix * viewProjection); _passClip.Apply(); data.ClipSubmesh.Draw(); graphicsDevice.DepthStencilState = lightNode.InvertClip ? GraphicsHelper.DepthStencilStateStencilEqual0 : GraphicsHelper.DepthStencilStateStencilNotEqual0; graphicsDevice.BlendState = GraphicsHelper.BlendStateAdd; } else { graphicsDevice.DepthStencilState = DepthStencilState.None; } if (hasShadow) { if (hasTexture) { if (light.Texture.Format == SurfaceFormat.Alpha8) { _passShadowedTexturedAlpha.Apply(); } else { _passShadowedTexturedRgb.Apply(); } } else { _passShadowed.Apply(); } } else { if (hasTexture) { if (light.Texture.Format == SurfaceFormat.Alpha8) { _passTexturedAlpha.Apply(); } else { _passTexturedRgb.Apply(); } } else { _passDefault.Apply(); } } graphicsDevice.DrawFullScreenQuad(); } savedRenderState.Restore(); }
public override void Render(IList <SceneNode> nodes, RenderContext context, RenderOrder order) { if (nodes == null) { throw new ArgumentNullException("nodes"); } if (context == null) { throw new ArgumentNullException("context"); } int numberOfNodes = nodes.Count; if (numberOfNodes == 0) { return; } context.Validate(_effect); context.ThrowIfCameraMissing(); var graphicsDevice = _effect.GraphicsDevice; var savedRenderState = new RenderStateSnapshot(graphicsDevice); graphicsDevice.DepthStencilState = DepthStencilState.None; graphicsDevice.RasterizerState = RasterizerState.CullNone; graphicsDevice.BlendState = GraphicsHelper.BlendStateAdd; var viewport = graphicsDevice.Viewport; _parameterViewportSize.SetValue(new Vector2(viewport.Width, viewport.Height)); _parameterGBuffer0.SetValue(context.GBuffer0); _parameterGBuffer1.SetValue(context.GBuffer1); var cameraNode = context.CameraNode; Matrix viewProjection = (Matrix)cameraNode.View * cameraNode.Camera.Projection; // Update SceneNode.LastFrame for all visible nodes. int frame = context.Frame; cameraNode.LastFrame = frame; var isHdrEnabled = context.IsHdrEnabled(); for (int i = 0; i < numberOfNodes; i++) { var lightNode = nodes[i] as LightNode; if (lightNode == null) { continue; } var light = lightNode.Light as AmbientLight; if (light == null) { continue; } // LightNode is visible in current frame. lightNode.LastFrame = frame; float hdrScale = isHdrEnabled ? light.HdrScale : 1; _parameterLightColor.SetValue((Vector3)light.Color * light.Intensity * hdrScale); _parameterHemisphericAttenuation.SetValue(light.HemisphericAttenuation); Vector3 upWorld = lightNode.PoseWorld.ToWorldDirection(Vector3.Up); _parameterUp.SetValue((Vector3)upWorld); if (lightNode.Clip != null) { var data = lightNode.RenderData as LightRenderData; if (data == null) { data = new LightRenderData(); lightNode.RenderData = data; } data.UpdateClipSubmesh(context.GraphicsService, lightNode); graphicsDevice.DepthStencilState = GraphicsHelper.DepthStencilStateOnePassStencilFail; graphicsDevice.BlendState = GraphicsHelper.BlendStateNoColorWrite; _parameterWorldViewProjection.SetValue((Matrix)data.ClipMatrix * viewProjection); _passClip.Apply(); data.ClipSubmesh.Draw(); graphicsDevice.DepthStencilState = lightNode.InvertClip ? GraphicsHelper.DepthStencilStateStencilEqual0 : GraphicsHelper.DepthStencilStateStencilNotEqual0; graphicsDevice.BlendState = GraphicsHelper.BlendStateAdd; } else { graphicsDevice.DepthStencilState = DepthStencilState.None; } _passLight.Apply(); graphicsDevice.DrawFullScreenQuad(); } savedRenderState.Restore(); }
public override void Render(IList <SceneNode> nodes, RenderContext context, RenderOrder order) { if (nodes == null) { throw new ArgumentNullException("nodes"); } if (context == null) { throw new ArgumentNullException("context"); } int numberOfNodes = nodes.Count; if (numberOfNodes == 0) { return; } context.Validate(_effect); context.ThrowIfCameraMissing(); var graphicsDevice = _effect.GraphicsDevice; var savedRenderState = new RenderStateSnapshot(graphicsDevice); graphicsDevice.DepthStencilState = DepthStencilState.None; graphicsDevice.RasterizerState = RasterizerState.CullNone; graphicsDevice.BlendState = GraphicsHelper.BlendStateAdd; var viewport = graphicsDevice.Viewport; _parameterViewportSize.SetValue(new Vector2(viewport.Width, viewport.Height)); _parameterGBuffer0.SetValue(context.GBuffer0); _parameterGBuffer1.SetValue(context.GBuffer1); var cameraNode = context.CameraNode; Pose cameraPose = cameraNode.PoseWorld; Matrix viewProjection = (Matrix)cameraNode.View * cameraNode.Camera.Projection; // Update SceneNode.LastFrame for all visible nodes. int frame = context.Frame; context.CameraNode.LastFrame = frame; bool isHdrEnabled = context.IsHdrEnabled(); // Copy nodes to list and sort them by persistent IDs. This is necessary to avoid popping when // light probes overlap. _jobs.Clear(); for (int i = 0; i < numberOfNodes; i++) { var lightNode = nodes[i] as LightNode; if (lightNode == null) { continue; } var light = lightNode.Light as ImageBasedLight; if (light == null || light.Texture == null) { continue; } // Build sort-ID - high values for lights which should be rendered last. ulong sortId = 0; // Render infinite lights first and others later. if (!(light.Shape is InfiniteShape)) { sortId += ((ulong)1 << 32); // Set high value above 32-bit range. } // Sort by priority. Lights with higher priority should be rendered last // (= over the other lights). // Shift priority (signed int) to positive range and add it. sortId += (ulong)((long)lightNode.Priority + int.MaxValue + 1); // Shift sortId and add light.Id in least significant bits. sortId = (sortId << 16) | (ushort)light.Id; // Add to list for sorting. _jobs.Add(new Job { SortId = sortId, LightNode = lightNode, }); } // Sort by ascending sort-ID value. _jobs.Sort(Comparer.Instance); numberOfNodes = _jobs.Count; for (int i = 0; i < numberOfNodes; i++) { var lightNode = _jobs[i].LightNode; var light = (ImageBasedLight)lightNode.Light; // LightNode is visible in current frame. lightNode.LastFrame = frame; // ReSharper disable CompareOfFloatsByEqualityOperator bool enableDiffuse = !(Numeric.IsNaN(light.DiffuseIntensity) || (light.DiffuseIntensity == 0.0f && light.BlendMode == 0.0f)); // ReSharper restore CompareOfFloatsByEqualityOperator bool enableSpecular = !Numeric.IsNaN(light.SpecularIntensity); if (!enableDiffuse && !enableSpecular) { continue; } float hdrScale = isHdrEnabled ? light.HdrScale : 1; // We use 1x1 mipmap level for diffuse. // (2x2 is still okay, 4x4 already looks a bit like a specular reflection.) float diffuseIntensity = enableDiffuse ? light.DiffuseIntensity : 0.0f; _parameterParameters0.SetValue(new Vector4( (Vector3)light.Color * diffuseIntensity * hdrScale, // DiffuseColor Math.Max(0, light.Texture.LevelCount - 1))); // Diffuse mip level. // Shader supports only RGBM. float rgbmMax; if (light.Encoding is RgbmEncoding) { rgbmMax = GraphicsHelper.ToGamma(((RgbmEncoding)light.Encoding).Max); } else if (light.Encoding is SRgbEncoding) { // Decoding RGBM with MaxValue 1 is equal to encoding sRGB, i.e. only // gamma-to-linear is performed (assuming that the cube map alpha channel is 1). rgbmMax = 1; } else { throw new NotSupportedException( "ImageBasedLight must use sRGB or RGBM encoding. Other encodings are not yet supported."); } _parameterParameters1.SetValue(new Vector4( (Vector3)light.Color * light.SpecularIntensity * hdrScale, // SpecularColor rgbmMax)); // Bounding box can be a box shape or an infinite shape. var boundingBoxShape = lightNode.Shape as BoxShape; // Get extent of bounding box. For infinite shapes we simply set a large value. var boundingBoxExtent = boundingBoxShape != null ? boundingBoxShape.Extent * lightNode.ScaleWorld : new Vector3(1e20f); // Falloff can only be used for box shapes but not for infinite shapes. float falloffRange = (boundingBoxShape != null) ? light.FalloffRange : 0; // AABB for localization in local space. // Use invalid min and max (min > max) to disable localization. Aabb projectionAabb = new Aabb(new Vector3(1), new Vector3(-1)); if (light.EnableLocalizedReflection) { if (light.LocalizedReflectionBox.HasValue) { // User defined AABB. projectionAabb = light.LocalizedReflectionBox.Value; projectionAabb.Minimum *= lightNode.ScaleWorld; projectionAabb.Maximum *= lightNode.ScaleWorld; } else if (boundingBoxShape != null) { // AABB is equal to the bounding box. projectionAabb = new Aabb(-boundingBoxExtent / 2, boundingBoxExtent / 2); } } _parameterParameters2.SetValue(new Vector4( boundingBoxExtent.X / 2, boundingBoxExtent.Y / 2, boundingBoxExtent.Z / 2, falloffRange)); _parameterParameters3.SetValue(new Vector4( projectionAabb.Minimum.X, projectionAabb.Minimum.Y, projectionAabb.Minimum.Z, light.Texture.Size)); _parameterParameters4.SetValue(new Vector4( projectionAabb.Maximum.X, projectionAabb.Maximum.Y, projectionAabb.Maximum.Z, light.BlendMode)); // Precomputed value for specular reflection lookup. const float sqrt3 = 1.7320508075688772935274463415059f; _parameterPrecomputedTerm.SetValue((float)Math.Log(light.Texture.Size * sqrt3, 2.0)); _parameterEnvironmentMap.SetValue(light.Texture); // Compute screen space rectangle and FrustumFarCorners. var rectangle = GraphicsHelper.GetViewportRectangle(cameraNode, viewport, lightNode); var texCoordTopLeft = new Vector2F(rectangle.Left / (float)viewport.Width, rectangle.Top / (float)viewport.Height); var texCoordBottomRight = new Vector2F(rectangle.Right / (float)viewport.Width, rectangle.Bottom / (float)viewport.Height); GraphicsHelper.GetFrustumFarCorners(cameraNode.Camera.Projection, texCoordTopLeft, texCoordBottomRight, _frustumFarCorners); // Convert frustum far corners from view space to world space. for (int j = 0; j < _frustumFarCorners.Length; j++) { _frustumFarCorners[j] = (Vector3)cameraPose.ToWorldDirection((Vector3)_frustumFarCorners[j]); } _parameterFrustumCorners.SetValue(_frustumFarCorners); EffectPass passLight = null; if (enableDiffuse && enableSpecular) { passLight = _passDiffuseAndSpecularLight; } else if (enableDiffuse) { // TODO: Can we disable writes to LightBuffer1? passLight = _passDiffuseLight; } else { // TODO: Can we disable writes to LightBuffer0? passLight = _passSpecularLight; } // Simply render fullscreen quad if we do not have a clip shape or a bounding box. if (lightNode.Clip == null && boundingBoxShape == null) { graphicsDevice.BlendState = BlendState.AlphaBlend; // Transform matrix transforms from world space with camera as origin to // local space. The lightNode.Scale is already in the other parameters and not // used in Transform. var pose = lightNode.PoseWorld; pose.Position -= cameraPose.Position; _parameterTransform.SetValue(pose.Inverse); passLight.Apply(); graphicsDevice.DrawFullScreenQuad(); continue; } // ----- Render clip mesh. graphicsDevice.DepthStencilState = GraphicsHelper.DepthStencilStateOnePassStencilFail; graphicsDevice.BlendState = GraphicsHelper.BlendStateNoColorWrite; if (lightNode.Clip != null) { // Using user-defined clip shape. var data = lightNode.RenderData as LightRenderData; if (data == null) { data = new LightRenderData(); lightNode.RenderData = data; } data.UpdateClipSubmesh(context.GraphicsService, lightNode); _parameterTransform.SetValue((Matrix)data.ClipMatrix * viewProjection); _passClip.Apply(); data.ClipSubmesh.Draw(); graphicsDevice.DepthStencilState = lightNode.InvertClip ? GraphicsHelper.DepthStencilStateStencilEqual0 : GraphicsHelper.DepthStencilStateStencilNotEqual0; } else { Debug.Assert(boundingBoxShape != null); // Use box submesh. if (_boxSubmesh == null) { _boxSubmesh = MeshHelper.GetBox(context.GraphicsService); } Matrix world = lightNode.PoseWorld * Matrix.CreateScale(lightNode.ScaleLocal * boundingBoxShape.Extent); _parameterTransform.SetValue((Matrix)world * viewProjection); _passClip.Apply(); _boxSubmesh.Draw(); graphicsDevice.DepthStencilState = GraphicsHelper.DepthStencilStateStencilNotEqual0; } graphicsDevice.BlendState = BlendState.AlphaBlend; { // Transform matrix transforms from world space with camera as origin to // local space. The lightNode.Scale is already in the other parameters and not // used in Transform. var pose = lightNode.PoseWorld; pose.Position -= cameraPose.Position; _parameterTransform.SetValue(pose.Inverse); } // ----- Render full screen quad. passLight.Apply(); graphicsDevice.DrawQuad(rectangle); } savedRenderState.Restore(); _jobs.Clear(); }