/// <summary> /// Creates a graphics mesh with the triangle mesh data of the given shape and the given /// diffuse and specular material properties. /// </summary> /// <param name="contentManager">The contentManager manager.</param> /// <param name="graphicsService">The graphics service.</param> /// <param name="shape">The shape.</param> /// <param name="diffuse">The diffuse material color.</param> /// <param name="specular">The specular material color.</param> /// <param name="specularPower">The specular power of the material.</param> /// <returns>The graphics mesh.</returns> public static Mesh CreateMesh(ContentManager contentManager, IGraphicsService graphicsService, Shape shape, Vector3 diffuse, Vector3 specular, float specularPower) { // Create a DigitalRune.Geometry.Meshes.TriangleMesh from the shape and // convert this to a DigitalRune.Graphics.Mesh. TriangleMesh triangleMesh = shape.GetMesh(0.01f, 4); Submesh submesh = CreateSubmeshWithTexCoords( graphicsService.GraphicsDevice, triangleMesh, MathHelper.ToRadians(70)); var mesh = CreateMesh(contentManager, graphicsService, submesh, diffuse, specular, specularPower); // Set bounding shape to a box that is equal to the AABB of the shape. var aabb = shape.GetAabb(Pose.Identity); var boxShape = new BoxShape(aabb.Extent); var center = aabb.Center; if (center.IsNumericallyZero) { mesh.BoundingShape = boxShape; } else { mesh.BoundingShape = new TransformedShape(new GeometricObject(boxShape, new Pose(center))); } return(mesh); }
public OcclusionCullingScreen(IServiceLocator services) : base(services) { _sceneNodes = new List <SceneNode>(); // Create new occlusion buffer with default settings. OcclusionBuffer = new OcclusionBuffer(GraphicsService); OcclusionBuffer.ProgressiveShadowCasterCulling = true; EnableCulling = true; // Create a second camera for rendering a top-down view of the scene. var topDownPerspective = new PerspectiveProjection(); topDownPerspective.SetFieldOfView(MathHelper.ToRadians(90), 1, 1, 512); _topDownCameraNode = new CameraNode(new Camera(topDownPerspective)); _topDownCameraNode.PoseWorld = new Pose(new Vector3F(-10, 120, -10)); _topDownCameraNode.LookAt(new Vector3F(-10, 0, -10), Vector3F.UnitZ); _sceneQuery = new CustomSceneQuery(); _debugRenderer = new DebugRenderer(GraphicsService, null, null); // The DigitalRune Profiler is used to measure execution times. Profiler.SetFormat("Occlusion.Render", 1e3f, "[ms]"); Profiler.SetFormat("Occlusion.Query", 1e3f, "[ms]"); }
// Creates a 32x32 texture which defines the water flow (direction + speed). private Texture2D GenerateFlowMap() { const int size = 32; var data = new Color[size * size]; for (int y = 0; y < size; y++) { for (int x = 0; x < size; x++) { Vector2F flowDirection; float flowSpeed; GetFlow(new Vector2F(x / (float)size, y / (float)size), out flowDirection, out flowSpeed); // Encode in color. The flow map stores the normalized 2D direction in r and g. // The speed (magnitude of the flow vector) is stored in b, where 0 represents // no movement and 1 represents movement with max speed. flowSpeed = MathHelper.Clamp(flowSpeed, 0, 1); // Convert to byte. data[y * size + x] = new Color( (byte)((flowDirection.X / 2 + 0.5f) * 255), (byte)((flowDirection.Y / 2 + 0.5f) * 255), (byte)(flowSpeed * 255)); } } var texture = new Texture2D(GraphicsService.GraphicsDevice, size, size, true, SurfaceFormat.Color); texture.SetData(data); return(texture); }
public Vector3 GetSunlight() { if (SunDirection.Y >= 0) { //----- Sun is above horizon. return(GetTransmittance(SunDirection) * SunIntensity); } //----- Sun is below horizon. // Get angle. float sunAngle = (float)MathHelper.ToDegrees(Math.Asin(SunDirection.Y)); if (sunAngle < -5) { return(Vector3.Zero); } // Sample horizon instead of real direction. Vector3 direction = SunDirection; direction.Y = 0; if (!direction.TryNormalize()) { return(Vector3.Zero); } Vector3 horizonSunlight = GetTransmittance(direction) * SunIntensity; // Lerp horizon sunlight to 0 at -5°. float f = 1 - MathHelper.Clamp(-sunAngle / 5.0f, 0, 1); return(horizonSunlight * f); }
public override void Update(GameTime gameTime) { // If <Space> is pressed, we fire a shot. if (InputService.IsPressed(Keys.Space, true)) { // Get a random angle and a random distance from the target. float angle = _angleDistribution.Next(_random); float distance = _distanceDistribution.Next(_random); // Create a vector v with the length of distance. Vector3 v = new Vector3(0, distance, 0); // Rotate v. Quaternion rotation = Quaternion.CreateRotationZ(MathHelper.ToRadians(angle)); v = rotation.Rotate(v); // Draw a small cross for the hit. var debugRenderer = GraphicsScreen.DebugRenderer2D; debugRenderer.DrawLine( _center + v + new Vector3(-10, -10, 0), _center + v + new Vector3(10, 10, 0), Color.Black, true); debugRenderer.DrawLine( _center + v + new Vector3(10, -10, 0), _center + v + new Vector3(-10, 10, 0), Color.Black, true); } base.Update(gameTime); }
public bool GetDisplacement(float x, float z, out Vector3 displacement, out Vector3 normal) { if (!EnableCpuQueries) throw new InvalidOperationException("OceanWaves.GetDisplacement() can only be called if EnableCpuQueries is set to true."); displacement = new Vector3(0); normal = new Vector3(0, 1, 0); if (_h == null) return false; float texCoordX = (x - TileCenter.X) / TileSize + 0.5f; float texCoordY = (z - TileCenter.Z) / TileSize + 0.5f; // Point sampling or bilinear filtering: // Convert to array indices. int xIndex = Wrap((int)(texCoordX * CpuSize)); int yIndex = Wrap((int)(texCoordY * CpuSize)); float h = _h[xIndex, yIndex].X; Vector2F d = _D[xIndex, yIndex]; Vector2F n = _N[xIndex, yIndex]; #else // Sample 4 values. The upper left index is (without wrapping): float xIndex = texCoordX * CpuSize - 0.5f; float yIndex = texCoordY * CpuSize - 0.5f; // Get the 4 indices. int x0 = Wrap((int)xIndex); int x1 = Wrap((int)xIndex + 1); int y0 = Wrap((int)yIndex); int y1 = Wrap((int)yIndex + 1); // Get fractions to use as lerp parameters. float px = MathHelper.Frac(xIndex); float py = MathHelper.Frac(yIndex); float h = InterpolationHelper.Lerp(InterpolationHelper.Lerp(_h[x0, y0].X, _h[x1, y0].X, px), InterpolationHelper.Lerp(_h[x0, y1].X, _h[x1, y1].X, px), py); Vector2F d = InterpolationHelper.Lerp(InterpolationHelper.Lerp(_D[x0, y0], _D[x1, y0], px), InterpolationHelper.Lerp(_D[x0, y1], _D[x1, y1], px), py); Vector2F n = InterpolationHelper.Lerp(InterpolationHelper.Lerp(_N[x0, y0], _N[x1, y0], px), InterpolationHelper.Lerp(_N[x0, y1], _N[x1, y1], px), py); displacement = new Vector3(-d.X * Choppiness, h, -d.Y * Choppiness); normal = new Vector3(-n.X, 0, -n.Y); normal.Y = (float)Math.Sqrt(1 - normal.X * normal.X - normal.Y * normal.Y); return true; }
public void GetScreenSizeWithPerspective() { // Camera var projection = new PerspectiveProjection(); projection.SetFieldOfView(MathHelper.ToRadians(90), 2.0f / 1.0f, 1.0f, 100f); var camera = new Camera(projection); var cameraNode = new CameraNode(camera); cameraNode.PoseWorld = new Pose(new Vector3(123, 456, -789), Matrix.CreateRotation(new Vector3(1, -2, 3), MathHelper.ToRadians(75))); // 2:1 viewport var viewport = new Viewport(10, 10, 200, 100); // Test object var shape = new SphereShape(); var geometricObject = new GeometricObject(shape); // Empty sphere at camera position. shape.Radius = 0; geometricObject.Pose = cameraNode.PoseWorld; Vector2F screenSize = GraphicsHelper.GetScreenSize(cameraNode, viewport, geometricObject); Assert.AreEqual(0, screenSize.X); Assert.AreEqual(0, screenSize.Y); // Empty sphere centered at near plane. shape.Radius = 0; geometricObject.Pose = cameraNode.PoseWorld * new Pose(new Vector3(0.123f, -0.543f, -1)); screenSize = GraphicsHelper.GetScreenSize(cameraNode, viewport, geometricObject); Assert.AreEqual(0, screenSize.X); Assert.AreEqual(0, screenSize.Y); // Create sphere which as a bounding sphere of ~1 unit diameter: // Since the bounding sphere is based on the AABB, we need to make the // actual sphere a bit smaller. shape.Radius = 1 / (2 * (float)Math.Sqrt(3)) + Numeric.EpsilonF; // Sphere at camera position. geometricObject.Pose = cameraNode.PoseWorld; screenSize = GraphicsHelper.GetScreenSize(cameraNode, viewport, geometricObject); Assert.Greater(screenSize.X, 200); Assert.Greater(screenSize.Y, 100); // Sphere at near plane. geometricObject.Pose = cameraNode.PoseWorld * new Pose(new Vector3(0.123f, -0.543f, -1)); screenSize = GraphicsHelper.GetScreenSize(cameraNode, viewport, geometricObject); Assert.IsTrue(Numeric.AreEqual(screenSize.X, 50.0f, 10f)); Assert.IsTrue(Numeric.AreEqual(screenSize.Y, 50.0f, 10f)); // Double distance --> half size geometricObject.Pose = cameraNode.PoseWorld * new Pose(new Vector3(0.123f, -0.543f, -2)); screenSize = GraphicsHelper.GetScreenSize(cameraNode, viewport, geometricObject); Assert.IsTrue(Numeric.AreEqual(screenSize.X, 25.0f, 5f)); Assert.IsTrue(Numeric.AreEqual(screenSize.Y, 25.0f, 5f)); }
// Called when Kinect has new skeleton data. private void OnSkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs eventArgs) { // Get the new skeleton data from Kinect. using (var skeletonFrame = eventArgs.OpenSkeletonFrame()) { if (skeletonFrame == null) { return; } if (_kinectSkeletons == null || _kinectSkeletons.Length != skeletonFrame.SkeletonArrayLength) { _kinectSkeletons = new KinectSkeleton[skeletonFrame.SkeletonArrayLength]; } skeletonFrame.CopySkeletonDataTo(_kinectSkeletons); } // Get the two tracked skeletons. KinectSkeleton skeletonDataA = null; KinectSkeleton skeletonDataB = null; foreach (var skeleton in _kinectSkeletons) { if (skeleton.TrackingState == SkeletonTrackingState.Tracked) { if (skeletonDataA == null) { skeletonDataA = skeleton; } else { skeletonDataB = skeleton; } } } // Make sure that each player uses the same skeleton as last time. // Swap skeleton data if necessary. if (skeletonDataA != null && skeletonDataA.TrackingId == _trackingIdB || skeletonDataB != null && skeletonDataB.TrackingId == _trackingIdA) { MathHelper.Swap(ref skeletonDataA, ref skeletonDataB); } // Update tracking IDs. _trackingIdA = (skeletonDataA != null) ? skeletonDataA.TrackingId : 0; _trackingIdB = (skeletonDataB != null) ? skeletonDataB.TrackingId : 0; // Update the SkeletonPose from the Kinect skeleton data. UpdateKinectSkeletonPose(skeletonDataA, SkeletonPoseA); UpdateKinectSkeletonPose(skeletonDataB, SkeletonPoseB); }
public override void Update(GameTime gameTime) { base.Update(gameTime); // <1> / <Shift> + <1> --> Change strength. if (InputService.IsDown(Keys.D1)) { bool isShiftDown = (InputService.ModifierKeys & ModifierKeys.Shift) != 0; float sign = isShiftDown ? +1 : -1; float time = (float)gameTime.ElapsedGameTime.TotalSeconds; float delta = sign * time * 0.2f; _grainFilter.Strength = Math.Max(0, _grainFilter.Strength + delta); } // <2> / <Shift> + <2> --> Change grain scale. if (InputService.IsDown(Keys.D2)) { bool isShiftDown = (InputService.ModifierKeys & ModifierKeys.Shift) != 0; float sign = isShiftDown ? +1 : -1; float time = (float)gameTime.ElapsedGameTime.TotalSeconds; float delta = sign * time * 0.2f; _grainFilter.GrainScale = Math.Max(1, _grainFilter.GrainScale + delta); } // <3> / <Shift> + <3> --> Change luminance threshold. if (InputService.IsDown(Keys.D3)) { bool isShiftDown = (InputService.ModifierKeys & ModifierKeys.Shift) != 0; float sign = isShiftDown ? +1 : -1; float time = (float)gameTime.ElapsedGameTime.TotalSeconds; float delta = sign * time * 0.2f; _grainFilter.LuminanceThreshold = MathHelper.Clamp(_grainFilter.LuminanceThreshold + delta, 0, 1); } // <4> --> Toggle ScaleWithLuminance. if (InputService.IsPressed(Keys.D4, false)) { _grainFilter.ScaleWithLuminance = !_grainFilter.ScaleWithLuminance; } // <5> --> Toggle IsAnimated. if (InputService.IsPressed(Keys.D5, false)) { _grainFilter.IsAnimated = !_grainFilter.IsAnimated; } GraphicsScreen.DebugRenderer.DrawText( "\n\nHold <1> or <Shift>+<1> to decrease or increase the grain strength: " + _grainFilter.Strength + "\nHold <2> or <Shift>+<2> to decrease or increase the grain scale: " + _grainFilter.GrainScale + "\nHold <3> or <Shift>+<3> to decrease or increase the luminance threshold: " + _grainFilter.LuminanceThreshold + "\nPress <4> to toggle 'scale with luminance': " + _grainFilter.ScaleWithLuminance + "\nPress <5> to toggle between animated and static noise: " + _grainFilter.IsAnimated); }
private void UpdateSteeringAngle(float deltaTime) { // TODO: Reduce max steering angle at high speeds. const float MaxAngle = 0.5f; const float SteeringRate = 3; // We limit the amount of change per frame. float change = SteeringRate * deltaTime; float direction = 0; if (_inputService.IsDown(Keys.A)) { direction += 1; } if (_inputService.IsDown(Keys.D)) { direction -= 1; } var gamePadState = _inputService.GetGamePadState(LogicalPlayerIndex.One); direction -= gamePadState.ThumbSticks.Left.X; if (direction != 0) { // Increase steering angle. _steeringAngle = MathHelper.Clamp(_steeringAngle + direction * change, -MaxAngle, +MaxAngle); } else { // Steer back to neutral position (angle 0). if (_steeringAngle > 0) { _steeringAngle = MathHelper.Clamp(_steeringAngle - change, 0, +MaxAngle); } else if (_steeringAngle < 0) { _steeringAngle = MathHelper.Clamp(_steeringAngle + change, -MaxAngle, 0); } // TODO: Maybe we steer back with half rate? // (Pressing a button steers faster than not pressing a button?) } VehicleHelper.SetCarSteeringAngle(_steeringAngle, Vehicle.Wheels[0], Vehicle.Wheels[1], Vehicle.Wheels[2], Vehicle.Wheels[3]); }
private void UpdateOrientation(float deltaTime) { GamePadState gamePadState = _inputService.GetGamePadState(LogicalPlayerIndex.One); // Compute new yaw and pitch from mouse movement and gamepad. float deltaYaw = -_inputService.MousePositionDelta.X; deltaYaw -= gamePadState.ThumbSticks.Right.X * ThumbStickFactor; _yaw += deltaYaw * deltaTime * AngularVelocityMagnitude; float deltaPitch = -_inputService.MousePositionDelta.Y; deltaPitch += gamePadState.ThumbSticks.Right.Y * ThumbStickFactor; _pitch += deltaPitch * deltaTime * AngularVelocityMagnitude; // Limit the pitch angle to +/- 90°. _pitch = MathHelper.Clamp(_pitch, -ConstantsF.PiOver2, ConstantsF.PiOver2); }
private void UpdateAcceleration(float deltaTime) { const float MaxForce = 2000; const float AccelerationRate = 10000; // We limit the amount of change per frame. float change = AccelerationRate * deltaTime; float direction = 0; if (_inputService.IsDown(Keys.W)) { direction += 1; } if (_inputService.IsDown(Keys.S)) { direction -= 1; } GamePadState gamePadState = _inputService.GetGamePadState(LogicalPlayerIndex.One); direction += gamePadState.Triggers.Right - gamePadState.Triggers.Left; if (direction != 0) { // Increase motor force. _motorForce = MathHelper.Clamp(_motorForce + direction * change, -MaxForce, +MaxForce); } else { // No acceleration. Bring motor force down to 0. if (_motorForce > 0) { _motorForce = MathHelper.Clamp(_motorForce - change, 0, +MaxForce); } else if (_motorForce < 0) { _motorForce = MathHelper.Clamp(_motorForce + change, -MaxForce, 0); } } // We can decide which wheels are motorized. Here we use an all wheel drive: Vehicle.Wheels[0].MotorForce = _motorForce; Vehicle.Wheels[1].MotorForce = _motorForce; Vehicle.Wheels[2].MotorForce = _motorForce; Vehicle.Wheels[3].MotorForce = _motorForce; }
// In this method, a vector is rotated with a quaternion and a matrix. The result // of the two vector rotations are compared. private void RotateVector() { var debugRenderer = GraphicsScreen.DebugRenderer2D; debugRenderer.DrawText("----- RotateVector Example:"); // Create a vector. We will rotate this vector. Vector3 v = new Vector3(1, 2, 3); // Create another vector which defines the axis of a rotation. Vector3 rotationAxis = Vector3.UnitZ; // The rotation angle in radians. We want to rotate 50°. float rotationAngle = MathHelper.ToRadians(50); // ----- Part 1: Rotate a vector with a quaternion. // Create a quaternion that represents a 50° rotation around the axis given // by rotationAxis. Quaternion rotation = Quaternion.CreateFromRotationMatrix(rotationAxis, rotationAngle); // Rotate the vector v using the rotation quaternion. Vector3 vRotated = rotation.Rotate(v); // ----- Part 2: Rotate a vector with a matrix. // Create a matrix that represents a 50° rotation around the axis given by // rotationAxis. Matrix rotationMatrix = Matrix.CreateRotation(rotationAxis, rotationAngle); // Rotate the vector v using the rotation matrix. Vector3 vRotated2 = rotationMatrix * v; // ----- Part 3: Compare the results. // The result of both rotations should be identical. // Because of numerical errors there can be minor differences in the results. // Therefore we use Vector3.AreNumericallyEqual() two check if the results // are equal (within a sensible numerical tolerance). if (Vector3.AreNumericallyEqual(vRotated, vRotated2)) { debugRenderer.DrawText("Vectors are equal.\n"); // This message is written. } else { debugRenderer.DrawText("Vectors are not equal.\n"); } }
// Returns the flow vector for a given position. // x and y are in the range [0, 1]. private static void GetFlow(Vector2F position, out Vector2F direction, out float speed) { // Create a circular movement around (0.5, 0.5). // Vector from center to position is: var radius = position - new Vector2F(0.5f, 0.5f); // The flow direction is orthogonal to the radius vector. direction = new Vector2F(radius.Y, -radius.X); direction.TryNormalize(); // The speed is max in the center and is 0 at the texture border. speed = 1; if (!Numeric.IsZero(radius.Length)) { speed = 1 - InterpolationHelper.HermiteSmoothStep(MathHelper.Clamp((radius.Length - 0.1f) / 0.4f, 0, 1)); } }
public override void Update(GameTime gameTime) { base.Update(gameTime); // <1> / <Shift> + <1> --> Decrease / Increase saturation. if (InputService.IsDown(Keys.D1)) { bool isShiftDown = (InputService.ModifierKeys & ModifierKeys.Shift) != 0; float sign = isShiftDown ? +1 : -1; float time = (float)gameTime.ElapsedGameTime.TotalSeconds; float delta = sign * time * 0.5f; _sepiaFilter.Strength = MathHelper.Clamp(_sepiaFilter.Strength + delta, 0, 1); } GraphicsScreen.DebugRenderer.DrawText( "\n\nHold <1> or <Shift>+<1> to decrease or increase the effect: " + _sepiaFilter.Strength); }
/// <overloads> /// <summary> /// Initializes a new instance of the <see cref="TerrainMaterialLayer"/> class. /// </summary> /// </overloads> /// /// <summary> /// Initializes a new instance of the <see cref="TerrainMaterialLayer"/> class with the default /// material. /// </summary> /// <param name="graphicService">The graphic service.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="graphicService"/> is <see langword="null"/>. /// </exception> public TerrainMaterialLayer(IGraphicsService graphicService) { if (graphicService == null) { throw new ArgumentNullException("graphicService"); } var effect = graphicService.Content.Load <Effect>("DigitalRune/Terrain/TerrainMaterialLayer"); Material = new Material { { "Detail", new EffectBinding(graphicService, effect, null, EffectParameterHint.Material) } }; FadeOutStart = int.MaxValue; FadeOutEnd = int.MaxValue; TileSize = 1; DiffuseColor = new Vector3(1, 1, 1); SpecularColor = new Vector3(1, 1, 1); SpecularPower = 10; Alpha = 1; DiffuseTexture = graphicService.GetDefaultTexture2DWhite(); SpecularTexture = graphicService.GetDefaultTexture2DBlack(); NormalTexture = graphicService.GetDefaultNormalTexture(); HeightTextureScale = 1; HeightTextureBias = 0; HeightTexture = graphicService.GetDefaultTexture2DBlack(); TriplanarTightening = -1; TintStrength = 1; TintTexture = graphicService.GetDefaultTexture2DWhite(); BlendThreshold = 0.5f; BlendRange = 1f; BlendHeightInfluence = 0; BlendNoiseInfluence = 0; BlendTextureChannel = 0; BlendTexture = graphicService.GetDefaultTexture2DWhite(); NoiseTileSize = 1; TerrainHeightMin = -1e20f; TerrainHeightMax = +1e20f; TerrainHeightBlendRange = 1f; TerrainSlopeMin = -ConstantsF.Pi; TerrainSlopeMax = ConstantsF.Pi; TerrainSlopeBlendRange = MathHelper.ToRadians(10); }
private Vector3 GetBaseColor(Vector3 direction) { // 0 = zenith, 1 = horizon float p = 1 - MathHelper.Clamp( (float)Math.Acos(direction.Y) / ConstantsF.Pi * 2, 0, 1); var colorAverage = (BaseHorizonColor + BaseZenithColor) / 2; if (p < BaseColorShift) { return(InterpolationHelper.Lerp(BaseHorizonColor, colorAverage, p / BaseColorShift)); } else { return(InterpolationHelper.Lerp(colorAverage, BaseZenithColor, (p - BaseColorShift) / (1 - BaseColorShift))); } }
protected override void OnUpdate(TimeSpan deltaTime) { // Mouse centering (controlled by the MenuComponent) is disabled if the game // is inactive or if the GUI is active. In these cases, we do not want to move // the player. if (!_inputService.EnableMouseCentering) { return; } if (_inputService.IsPressed(Keys.Enter, true)) { // Toggle between player camera and spectator view. _useSpectatorView = !_useSpectatorView; } else { float deltaTimeF = (float)deltaTime.TotalSeconds; // Compute new yaw and pitch from mouse movement. float deltaYaw = 0; deltaYaw -= _inputService.MousePositionDelta.X; deltaYaw -= _inputService.GetGamePadState(LogicalPlayerIndex.One).ThumbSticks.Right.X * 10; _yaw += deltaYaw * deltaTimeF * 0.1f; float deltaPitch = 0; deltaPitch -= _inputService.MousePositionDelta.Y; deltaPitch += _inputService.GetGamePadState(LogicalPlayerIndex.One).ThumbSticks.Right.Y * 10; _pitch += deltaPitch * deltaTimeF * 0.1f; // Limit the pitch angle to less than +/- 90°. float limit = ConstantsF.PiOver2 - 0.01f; _pitch = MathHelper.Clamp(_pitch, -limit, limit); } // Update SceneNode.LastPoseWorld - this is required for some effects, like // camera motion blur. CameraNode.LastPoseWorld = CameraNode.PoseWorld; SetCameraPose(); }
public void UnprojectTest() { Viewport viewport = new Viewport(0, 0, 640, 480); PerspectiveProjection projection = new PerspectiveProjection(); projection.SetFieldOfView(MathHelper.ToRadians(60), viewport.AspectRatio, 10, 1000); Matrix view = Matrix.CreateLookAt(new Vector3(0, 0, 0), new Vector3(0, 0, -1), Vector3.Up); Assert.IsTrue(Vector3.AreNumericallyEqual(new Vector3(0, 0, -10), viewport.Unproject(new Vector3(320, 240, 0), projection, view))); Assert.IsTrue(Vector3.AreNumericallyEqual(new Vector3(projection.Left, projection.Top, -10), viewport.Unproject(new Vector3(0, 0, 0), projection, view))); Assert.IsTrue(Vector3.AreNumericallyEqual(new Vector3(projection.Right, projection.Top, -10), viewport.Unproject(new Vector3(640, 0, 0), projection, view))); Assert.IsTrue(Vector3.AreNumericallyEqual(new Vector3(projection.Left, projection.Bottom, -10), viewport.Unproject(new Vector3(0, 480, 0), projection, view))); Assert.IsTrue(Vector3.AreNumericallyEqual(new Vector3(projection.Right, projection.Bottom, -10), viewport.Unproject(new Vector3(640, 480, 0), projection, view))); Vector3[] farCorners = new Vector3[4]; GraphicsHelper.GetFrustumFarCorners(projection, farCorners); Assert.IsTrue(Vector3.AreNumericallyEqual((Vector3)farCorners[0], viewport.Unproject(new Vector3(0, 0, 1), projection, view))); Assert.IsTrue(Vector3.AreNumericallyEqual((Vector3)farCorners[1], viewport.Unproject(new Vector3(640, 0, 1), projection, view))); Assert.IsTrue(Vector3.AreNumericallyEqual((Vector3)farCorners[2], viewport.Unproject(new Vector3(0, 480, 1), projection, view))); Assert.IsTrue(Vector3.AreNumericallyEqual((Vector3)farCorners[3], viewport.Unproject(new Vector3(640, 480, 1), projection, view))); }
// This method shows how to safely compare vectors. private void CompareVectors() { var debugRenderer = GraphicsScreen.DebugRenderer2D; debugRenderer.DrawText("----- CompareVectors Example:"); // Define a vector. Vector3 v0 = new Vector3(1000, 2000, 3000); // Define a rotation quaternion that rotates 360° around the x axis. Quaternion rotation = Quaternion.CreateRotationX(MathHelper.ToRadians(360)); // Rotate v0. Vector3 v1 = rotation.Rotate(v0); // The rotated vector v1 should be identical to v0 because a 360° rotation // should not change the vector. - But due to numerical errors v0 and v1 are // not equal. if (v0 == v1) { debugRenderer.DrawText("Vectors are equal."); } else { debugRenderer.DrawText("Vectors are not equal."); // This message is written. } // With Vector3.AreNumericallyEqual() we can check if two vectors are equal // when we allow a small numeric tolerance. The tolerance that is applied is // Numeric.EpsilonF, e.g. 10^-5. if (Vector3.AreNumericallyEqual(v0, v1)) { debugRenderer.DrawText("Vectors are numerically equal.\n"); // This message is written. } else { debugRenderer.DrawText("Vectors are not numerically equal.\n"); } }
public override void Update(GameTime gameTime) { base.Update(gameTime); var oldMode = _mode; var oldUseHardwareFiltering = _useHardwareFiltering; // <1> / <Shift> + <1> --> Change mode. if (InputService.IsPressed(Keys.D1, true)) { bool isShiftDown = (InputService.ModifierKeys & ModifierKeys.Shift) != 0; if (isShiftDown) { _mode++; } else { _mode--; } } // <2> / <Shift> + <2> --> Change number of passes. if (InputService.IsPressed(Keys.D2, true)) { bool isShiftDown = (InputService.ModifierKeys & ModifierKeys.Shift) != 0; if (isShiftDown) { _blur.NumberOfPasses++; } else { _blur.NumberOfPasses = Math.Max(1, _blur.NumberOfPasses - 1); } } // <3> / <Shift> + <3> --> Change hardware filtering. if (InputService.IsPressed(Keys.D3, true)) { _useHardwareFiltering = !_useHardwareFiltering; } // <4> / <Shift> + <4> --> Change scale. if (InputService.IsDown(Keys.D4)) { // Increase or decrease value by a factor of 1.01 every frame (1/60 s). bool isShiftDown = (InputService.ModifierKeys & ModifierKeys.Shift) != 0; float factor = isShiftDown ? 1.01f : 1.0f / 1.01f; float time = (float)gameTime.ElapsedGameTime.TotalSeconds; _blur.Scale *= (float)Math.Pow(factor, time * 60); } // Update blur mode. _mode = MathHelper.Clamp(_mode, 0, 2); if (oldMode != _mode || oldUseHardwareFiltering != _useHardwareFiltering) { if (_mode == 0) { _modeName = "15-tap Box Blur"; _blur.InitializeBoxBlur(15, _useHardwareFiltering); } else if (_mode == 1) { _modeName = "15-tap Gaussian Blur"; _blur.InitializeGaussianBlur(15, 15.0f / 6, _useHardwareFiltering); } else if (_mode == 2) { _modeName = "13-tap Poisson Blur"; _blur.InitializePoissonBlur(); } } GraphicsScreen.DebugRenderer.DrawText( "\n\nPress <1> or <Shift>+<1> to change the blur kernel: " + _modeName + "\nPress <2> or <Shift>+<2> to decrease or increase the number of passes: " + _blur.NumberOfPasses + "\nPress <3> toggle hardware filtering: " + _useHardwareFiltering + "\nPress <4> or <Shift>+<4> to decrease or increase the blur scale: " + _blur.Scale); }
//-------------------------------------------------------------- #region Methods //-------------------------------------------------------------- /// <summary> /// Computes the intersection of <see cref="MeshNode"/>s. /// </summary> /// <param name="meshNodePairs"> /// A collection of <see cref="MeshNode"/> pairs.The renderer computes the intersection volume /// of each pair. /// </param> /// <param name="color">The diffuse color used for the intersection.</param> /// <param name="alpha">The opacity of the intersection.</param> /// <param name="maxConvexity"> /// The maximum convexity of the submeshes. A convex mesh has a convexity of 1. A concave mesh /// has a convexity greater than 1. Convexity is the number of layers required for depth peeling /// (= the number of front face layers when looking at the object). /// </param> /// <param name="context">The render context.</param> /// <remarks> /// <para> /// This method renders an off-screen image (color and depth) of the intersection volume. This /// operation destroys the currently set render target and depth/stencil buffer. /// </para> /// </remarks> /// <exception cref="ObjectDisposedException"> /// The <see cref="IntersectionRenderer"/> has already been disposed. /// </exception> /// <exception cref="ArgumentNullException"> /// <paramref name="meshNodePairs"/> or <see cref="context"/> is /// <see langword="null"/>. /// </exception> /// <exception cref="ArgumentOutOfRangeException"> /// The convexity must be greater than 0. /// </exception> /// <exception cref="GraphicsException"> /// Invalid render context: Graphics service is not set. /// </exception> /// <exception cref="GraphicsException"> /// Invalid render context: Wrong graphics device. /// </exception> /// <exception cref="GraphicsException"> /// Invalid render context: Scene is not set. /// </exception> /// <exception cref="GraphicsException"> /// Invalid render context: Camera node needs to be set in render context. /// </exception> public void ComputeIntersection(IEnumerable <Pair <MeshNode> > meshNodePairs, Vector3F color, float alpha, float maxConvexity, RenderContext context) { if (_isDisposed) { throw new ObjectDisposedException("IntersectionRenderer has already been disposed."); } if (meshNodePairs == null) { throw new ArgumentNullException("meshNodePairs"); } if (maxConvexity < 1) { throw new ArgumentOutOfRangeException("maxConvexity", "The max convexity must be greater than 0."); } if (context == null) { throw new ArgumentNullException("context"); } if (context.GraphicsService == null) { throw new GraphicsException("Invalid render context: Graphics service is not set."); } if (_graphicsService != context.GraphicsService) { throw new GraphicsException("Invalid render context: Wrong graphics service."); } if (context.CameraNode == null) { throw new GraphicsException("Camera node needs to be set in render context."); } if (context.Scene == null) { throw new GraphicsException("A scene needs to be set in the render context."); } // Create 2 ordered pairs for each unordered pair. _pairs.Clear(); foreach (var pair in meshNodePairs) { if (pair.First == null || pair.Second == null) { continue; } // Frustum culling. if (!context.Scene.HaveContact(pair.First, context.CameraNode)) { continue; } if (!context.Scene.HaveContact(pair.Second, context.CameraNode)) { continue; } _pairs.Add(new Pair <MeshNode, MeshNode>(pair.First, pair.Second)); _pairs.Add(new Pair <MeshNode, MeshNode>(pair.Second, pair.First)); } var renderTargetPool = _graphicsService.RenderTargetPool; if (_pairs.Count == 0) { renderTargetPool.Recycle(_intersectionImage); _intersectionImage = null; return; } // Color and alpha are applied in RenderIntersection(). _color = color; _alpha = alpha; var graphicsDevice = _graphicsService.GraphicsDevice; // Save original render states. var originalBlendState = graphicsDevice.BlendState; var originalDepthStencilState = graphicsDevice.DepthStencilState; var originalRasterizerState = graphicsDevice.RasterizerState; var originalScissorRectangle = graphicsDevice.ScissorRectangle; // Get offscreen render targets. var viewport = context.Viewport; viewport.X = 0; viewport.Y = 0; viewport.Width = (int)(viewport.Width / DownsampleFactor); viewport.Height = (int)(viewport.Height / DownsampleFactor); var renderTargetFormat = new RenderTargetFormat(viewport.Width, viewport.Height, false, SurfaceFormat.Color, DepthFormat.Depth24Stencil8); // Try to reuse any existing render targets. // (Usually they are recycled in RenderIntersection()). var currentScene = _intersectionImage; if (currentScene == null || !renderTargetFormat.IsCompatibleWith(currentScene)) { currentScene.SafeDispose(); currentScene = renderTargetPool.Obtain2D(renderTargetFormat); } var lastScene = renderTargetPool.Obtain2D(renderTargetFormat); // Set shared effect parameters. var cameraNode = context.CameraNode; var view = (Matrix)cameraNode.View; var projection = cameraNode.Camera.Projection; var near = projection.Near; var far = projection.Far; _parameterViewportSize.SetValue(new Vector2(viewport.Width, viewport.Height)); // The DepthEpsilon has to be tuned if depth peeling does not work because // of numerical problems equality z comparisons. _parameterCameraParameters.SetValue(new Vector3(near, far - near, 0.0000001f)); _parameterView.SetValue(view); _parameterProjection.SetValue((Matrix)projection); var defaultTexture = _graphicsService.GetDefaultTexture2DBlack(); // Handle all pairs. bool isFirstPass = true; while (true) { // Find a mesh node A and all mesh nodes to which it needs to be clipped. MeshNode meshNodeA = null; _partners.Clear(); for (int i = 0; i < _pairs.Count; i++) { var pair = _pairs[i]; if (pair.First == null) { continue; } if (meshNodeA == null) { meshNodeA = pair.First; } if (pair.First == meshNodeA) { _partners.Add(pair.Second); // Remove this pair. _pairs[i] = new Pair <MeshNode, MeshNode>(); } } // Abort if we have handled all pairs. if (meshNodeA == null) { break; } var worldTransformA = (Matrix)(meshNodeA.PoseWorld * Matrix44F.CreateScale(meshNodeA.ScaleWorld)); if (EnableScissorTest) { // Scissor rectangle of A. var scissorA = GraphicsHelper.GetScissorRectangle(context.CameraNode, viewport, meshNodeA); // Union of scissor rectangles of partners. Rectangle partnerRectangle = GraphicsHelper.GetScissorRectangle(context.CameraNode, viewport, _partners[0]); for (int i = 1; i < _partners.Count; i++) { var a = GraphicsHelper.GetScissorRectangle(context.CameraNode, viewport, _partners[i]); partnerRectangle = Rectangle.Union(partnerRectangle, a); } // Use intersection of A and partners. graphicsDevice.ScissorRectangle = Rectangle.Intersect(scissorA, partnerRectangle); // We store the union of all scissor rectangles for use in RenderIntersection(). if (isFirstPass) { _totalScissorRectangle = graphicsDevice.ScissorRectangle; } else { _totalScissorRectangle = Rectangle.Union(_totalScissorRectangle, graphicsDevice.ScissorRectangle); } } // Depth peeling of A. for (int layer = 0; layer < maxConvexity; layer++) { // Set and clear render target. graphicsDevice.SetRenderTarget(currentScene); graphicsDevice.Clear(new Color(1, 1, 1, 0)); // RGB = "a large depth", A = "empty area" // Render a depth layer of A. graphicsDevice.DepthStencilState = DepthStencilStateWriteLess; graphicsDevice.BlendState = BlendState.Opaque; graphicsDevice.RasterizerState = EnableScissorTest ? CullCounterClockwiseScissor : RasterizerState.CullCounterClockwise; _parameterWorld.SetValue(worldTransformA); _parameterTexture.SetValue((layer == 0) ? defaultTexture : lastScene); _passPeel.Apply(); foreach (var submesh in meshNodeA.Mesh.Submeshes) { submesh.Draw(); } // Render partners to set stencil. graphicsDevice.DepthStencilState = DepthStencilStateOnePassStencilFail; graphicsDevice.BlendState = BlendStateNoWrite; graphicsDevice.RasterizerState = EnableScissorTest ? CullNoneScissor : RasterizerState.CullNone; foreach (var partner in _partners) { _parameterWorld.SetValue((Matrix)(partner.PoseWorld * Matrix44F.CreateScale(partner.ScaleWorld))); _passMark.Apply(); foreach (var submesh in partner.Mesh.Submeshes) { submesh.Draw(); } } // Clear depth buffer. Leave stencil buffer unchanged. graphicsDevice.Clear(ClearOptions.DepthBuffer, new Color(0, 1, 0), 1, 0); // Render A to compute lighting. graphicsDevice.DepthStencilState = DepthStencilStateStencilNotEqual0; graphicsDevice.BlendState = BlendState.Opaque; graphicsDevice.RasterizerState = EnableScissorTest ? CullCounterClockwiseScissor : RasterizerState.CullCounterClockwise; _parameterWorld.SetValue(worldTransformA); _passDraw.Apply(); foreach (var submesh in meshNodeA.Mesh.Submeshes) { submesh.Draw(); } // Combine last intersection image with current. if (!isFirstPass) { graphicsDevice.DepthStencilState = DepthStencilState.DepthRead; graphicsDevice.BlendState = BlendState.Opaque; graphicsDevice.RasterizerState = EnableScissorTest ? CullNoneScissor : RasterizerState.CullNone; _parameterTexture.SetValue(lastScene); _passCombine.Apply(); graphicsDevice.DrawFullScreenQuad(); } isFirstPass = false; // ----- Swap render targets. MathHelper.Swap(ref lastScene, ref currentScene); } } // Store final images for RenderIntersection. _intersectionImage = lastScene; // Scale scissor rectangle back to full-screen resolution. if (DownsampleFactor > 1) { _totalScissorRectangle.X = (int)(_totalScissorRectangle.X * DownsampleFactor); _totalScissorRectangle.Y = (int)(_totalScissorRectangle.Y * DownsampleFactor); _totalScissorRectangle.Width = (int)(_totalScissorRectangle.Width * DownsampleFactor); _totalScissorRectangle.Height = (int)(_totalScissorRectangle.Height * DownsampleFactor); } // Restore original render state. graphicsDevice.BlendState = originalBlendState ?? BlendState.Opaque; graphicsDevice.DepthStencilState = originalDepthStencilState ?? DepthStencilState.Default; graphicsDevice.RasterizerState = originalRasterizerState ?? RasterizerState.CullCounterClockwise; graphicsDevice.ScissorRectangle = originalScissorRectangle; renderTargetPool.Recycle(currentScene); _partners.Clear(); _pairs.Clear(); }
// Creates a lot of random objects. private void CreateRandomObjects() { var random = new Random(); var isFirstHeightField = true; int currentShape = 0; int numberOfObjects = 0; while (true) { numberOfObjects++; if (numberOfObjects > ObjectsPerType) { currentShape++; numberOfObjects = 0; } Shape shape; switch (currentShape) { case 0: // Box shape = new BoxShape(ObjectSize, ObjectSize * 2, ObjectSize * 3); break; case 1: // Capsule shape = new CapsuleShape(0.3f * ObjectSize, 2 * ObjectSize); break; case 2: // Cone shape = new ConeShape(1 * ObjectSize, 2 * ObjectSize); break; case 3: // Cylinder shape = new CylinderShape(0.4f * ObjectSize, 2 * ObjectSize); break; case 4: // Sphere shape = new SphereShape(ObjectSize); break; case 5: // Convex hull of several points. ConvexHullOfPoints hull = new ConvexHullOfPoints(); hull.Points.Add(new Vector3(-1 * ObjectSize, -2 * ObjectSize, -1 * ObjectSize)); hull.Points.Add(new Vector3(2 * ObjectSize, -1 * ObjectSize, -0.5f * ObjectSize)); hull.Points.Add(new Vector3(1 * ObjectSize, 2 * ObjectSize, 1 * ObjectSize)); hull.Points.Add(new Vector3(-1 * ObjectSize, 2 * ObjectSize, 1 * ObjectSize)); hull.Points.Add(new Vector3(-1 * ObjectSize, 0.7f * ObjectSize, -0.6f * ObjectSize)); shape = hull; break; case 6: // A composite shape: two boxes that form a "T" shape. var composite = new CompositeShape(); composite.Children.Add( new GeometricObject( new BoxShape(ObjectSize, 3 * ObjectSize, ObjectSize), new Pose(new Vector3(0, 0, 0)))); composite.Children.Add( new GeometricObject( new BoxShape(2 * ObjectSize, ObjectSize, ObjectSize), new Pose(new Vector3(0, 2 * ObjectSize, 0)))); shape = composite; break; case 7: shape = new CircleShape(ObjectSize); break; case 8: { var compBvh = new CompositeShape(); compBvh.Children.Add(new GeometricObject(new BoxShape(0.5f, 1, 0.5f), new Pose(new Vector3(0, 0.5f, 0), Matrix.Identity))); compBvh.Children.Add(new GeometricObject(new BoxShape(0.8f, 0.5f, 0.5f), new Pose(new Vector3(0.5f, 0.7f, 0), Matrix.CreateRotationZ(-MathHelper.ToRadians(15))))); compBvh.Children.Add(new GeometricObject(new SphereShape(0.3f), new Pose(new Vector3(0, 1.15f, 0), Matrix.Identity))); compBvh.Children.Add(new GeometricObject(new CapsuleShape(0.2f, 1), new Pose(new Vector3(0.6f, 1.15f, 0), Matrix.CreateRotationX(0.3f)))); compBvh.Partition = new AabbTree<int>(); shape = compBvh; break; } case 9: CompositeShape comp = new CompositeShape(); comp.Children.Add(new GeometricObject(new BoxShape(0.5f * ObjectSize, 1 * ObjectSize, 0.5f * ObjectSize), new Pose(new Vector3(0, 0.5f * ObjectSize, 0), Quaternion.Identity))); comp.Children.Add(new GeometricObject(new BoxShape(0.8f * ObjectSize, 0.5f * ObjectSize, 0.5f * ObjectSize), new Pose(new Vector3(0.3f * ObjectSize, 0.7f * ObjectSize, 0), Quaternion.CreateRotationZ(-MathHelper.ToRadians(45))))); comp.Children.Add(new GeometricObject(new SphereShape(0.3f * ObjectSize), new Pose(new Vector3(0, 1.15f * ObjectSize, 0), Quaternion.Identity))); shape = comp; break; case 10: shape = new ConvexHullOfPoints(new[] { new Vector3(-1 * ObjectSize, -2 * ObjectSize, -1 * ObjectSize), new Vector3(2 * ObjectSize, -1 * ObjectSize, -0.5f * ObjectSize), new Vector3(1 * ObjectSize, 2 * ObjectSize, 1 * ObjectSize), new Vector3(-1 * ObjectSize, 2 * ObjectSize, 1 * ObjectSize), new Vector3(-1 * ObjectSize, 0.7f * ObjectSize, -0.6f * ObjectSize) }); break; case 11: ConvexHullOfShapes shapeHull = new ConvexHullOfShapes(); shapeHull.Children.Add(new GeometricObject(new SphereShape(0.3f * ObjectSize), new Pose(new Vector3(0, 2 * ObjectSize, 0), Matrix.Identity))); shapeHull.Children.Add(new GeometricObject(new BoxShape(1 * ObjectSize, 2 * ObjectSize, 3 * ObjectSize), Pose.Identity)); shape = shapeHull; break; case 12: shape = Shape.Empty; break; case 13: var numberOfSamplesX = 10; var numberOfSamplesZ = 10; var samples = new float[numberOfSamplesX * numberOfSamplesZ]; for (int z = 0; z < numberOfSamplesZ; z++) for (int x = 0; x < numberOfSamplesX; x++) samples[z * numberOfSamplesX + x] = (float)(Math.Cos(z / 3f) * Math.Sin(x / 2f) * BoxSize / 6); HeightField heightField = new HeightField(0, 0, 2 * BoxSize, 2 * BoxSize, samples, numberOfSamplesX, numberOfSamplesZ); shape = heightField; break; //case 14: //shape = new LineShape(new Vector3(0.1f, 0.2f, 0.3f), new Vector3(0.1f, 0.2f, -0.3f).Normalized); //break; case 15: shape = new LineSegmentShape( new Vector3(0.1f, 0.2f, 0.3f), new Vector3(0.1f, 0.2f, 0.3f) + 3 * ObjectSize * new Vector3(0.1f, 0.2f, -0.3f)); break; case 16: shape = new MinkowskiDifferenceShape { ObjectA = new GeometricObject(new SphereShape(0.1f * ObjectSize)), ObjectB = new GeometricObject(new BoxShape(1 * ObjectSize, 2 * ObjectSize, 3 * ObjectSize)) }; break; case 17: shape = new MinkowskiSumShape { ObjectA = new GeometricObject(new SphereShape(0.1f * ObjectSize)), ObjectB = new GeometricObject(new BoxShape(1 * ObjectSize, 2 * ObjectSize, 3 * ObjectSize)), }; break; case 18: shape = new OrthographicViewVolume(0, ObjectSize, 0, ObjectSize, ObjectSize / 2, ObjectSize * 2); break; case 19: shape = new PerspectiveViewVolume(MathHelper.ToRadians(60f), 16f / 10, ObjectSize / 2, ObjectSize * 3); break; case 20: shape = new PointShape(0.1f, 0.3f, 0.2f); break; case 21: shape = new RayShape(new Vector3(0.2f, 0, -0.12f), new Vector3(1, 2, 3).Normalized, ObjectSize * 2); break; case 22: shape = new RayShape(new Vector3(0.2f, 0, -0.12f), new Vector3(1, 2, 3).Normalized, ObjectSize * 2) { StopsAtFirstHit = true }; break; case 23: shape = new RectangleShape(ObjectSize, ObjectSize * 2); break; case 24: shape = new TransformedShape( new GeometricObject( new BoxShape(1 * ObjectSize, 2 * ObjectSize, 3 * ObjectSize), new Pose(new Vector3(0.1f, 1, -0.2f)))); break; case 25: shape = new TriangleShape( new Vector3(ObjectSize, 0, 0), new Vector3(0, ObjectSize, 0), new Vector3(ObjectSize, ObjectSize, ObjectSize)); break; //case 26: // { // // Create a composite object from which we get the mesh. // CompositeShape compBvh = new CompositeShape(); // compBvh.Children.Add(new GeometricObject(new BoxShape(0.5f, 1, 0.5f), new Pose(new Vector3(0, 0.5f, 0), Matrix.Identity))); // compBvh.Children.Add( // new GeometricObject( // new BoxShape(0.8f, 0.5f, 0.5f), // new Pose(new Vector3(0.5f, 0.7f, 0), Matrix.CreateRotationZ(-(float)MathHelper.ToRadians(15))))); // compBvh.Children.Add(new GeometricObject(new SphereShape(0.3f), new Pose(new Vector3(0, 1.15f, 0), Matrix.Identity))); // compBvh.Children.Add( // new GeometricObject(new CapsuleShape(0.2f, 1), new Pose(new Vector3(0.6f, 1.15f, 0), Matrix.CreateRotationX(0.3f)))); // TriangleMeshShape meshBvhShape = new TriangleMeshShape { Mesh = compBvh.GetMesh(0.01f, 3) }; // meshBvhShape.Partition = new AabbTree<int>(); // shape = meshBvhShape; // break; // } //case 27: // { // // Create a composite object from which we get the mesh. // CompositeShape compBvh = new CompositeShape(); // compBvh.Children.Add(new GeometricObject(new BoxShape(0.5f, 1, 0.5f), new Pose(new Vector3(0, 0.5f, 0), Quaternion.Identity))); // compBvh.Children.Add( // new GeometricObject( // new BoxShape(0.8f, 0.5f, 0.5f), // new Pose(new Vector3(0.5f, 0.7f, 0), Quaternion.CreateRotationZ(-(float)MathHelper.ToRadians(15))))); // compBvh.Children.Add(new GeometricObject(new SphereShape(0.3f), new Pose(new Vector3(0, 1.15f, 0), Quaternion.Identity))); // compBvh.Children.Add( // new GeometricObject(new CapsuleShape(0.2f, 1), new Pose(new Vector3(0.6f, 1.15f, 0), Quaternion.CreateRotationX(0.3f)))); // TriangleMeshShape meshBvhShape = new TriangleMeshShape { Mesh = compBvh.GetMesh(0.01f, 3) }; // meshBvhShape.Partition = new AabbTree<int>(); // shape = meshBvhShape; // break; // } case 28: shape = new ConvexPolyhedron(new[] { new Vector3(-1 * ObjectSize, -2 * ObjectSize, -1 * ObjectSize), new Vector3(2 * ObjectSize, -1 * ObjectSize, -0.5f * ObjectSize), new Vector3(1 * ObjectSize, 2 * ObjectSize, 1 * ObjectSize), new Vector3(-1 * ObjectSize, 2 * ObjectSize, 1 * ObjectSize), new Vector3(-1 * ObjectSize, 0.7f * ObjectSize, -0.6f * ObjectSize) }); break; case 29: return; default: currentShape++; continue; } // Create an object with the random shape, pose, color and velocity. Pose randomPose = new Pose( random.NextVector3(-BoxSize + ObjectSize * 2, BoxSize - ObjectSize * 2), random.NextQuaternion()); var newObject = new MovingGeometricObject { Pose = randomPose, Shape = shape, LinearVelocity = random.NextQuaternion().Rotate(new Vector3(MaxLinearVelocity, 0, 0)), AngularVelocity = random.NextQuaternion().Rotate(Vector3.Forward) * RandomHelper.Random.NextFloat(0, MaxAngularVelocity), }; if (RandomHelper.Random.NextBool()) newObject.LinearVelocity = Vector3.Zero; if (RandomHelper.Random.NextBool()) newObject.AngularVelocity = Vector3.Zero; if (shape is LineShape || shape is HeightField) { // Do not move lines or the height field. newObject.LinearVelocity = Vector3.Zero; newObject.AngularVelocity = Vector3.Zero; } // Create only 1 heightField! if (shape is HeightField) { if (isFirstHeightField) { isFirstHeightField = true; newObject.Pose = new Pose(new Vector3(-BoxSize, -BoxSize, -BoxSize)); } else { currentShape++; numberOfObjects = 0; continue; } } // Add collision object to collision domain. _domain.CollisionObjects.Add(new CollisionObject(newObject)); //co.Type = CollisionObjectType.Trigger; //co.Name = "Object" + shape.GetType().Name + "_" + i; } }
private void InitializeGaussianBlur(Vector2F viewportSize, bool useHardwareFiltering) { if (_horizontalOffsets != null && _lastViewportSize == viewportSize) { return; } _lastViewportSize = viewportSize; int numberOfSamples = _offsetsParameter.Elements.Count; float standardDeviation = numberOfSamples / 3.0f * BlurStrength; if (_horizontalOffsets == null) { _horizontalOffsets = new Vector2[numberOfSamples]; _verticalOffsets = new Vector2[numberOfSamples]; _weights = new float[numberOfSamples]; } // Define the Gaussian function coefficient that we use. float coefficient = 1 / (float)Math.Sqrt(ConstantsF.TwoPi) / standardDeviation; float weightSum; if (useHardwareFiltering) { // We sample 2 pixels per tap, so we can sample twice as wide. standardDeviation = standardDeviation * 2; // Sample the center pixel in the middle and then between pixel. _horizontalOffsets[0] = new Vector2(0, 0); _verticalOffsets[0] = new Vector2(0, 0); _weights[0] = (BlurStrength > 0) ? MathHelper.Gaussian(0, coefficient, 0, standardDeviation) : 1; weightSum = _weights[0]; for (int i = 1; i < numberOfSamples; i += 2) { // Get an offset between two pixels. var offset = new Vector2(1.5f + (i - 1), 0); // = 1.5 + k * 2 // Get the offsets of the neighboring pixel centers. var o0 = offset.X - 0.5f; var o1 = offset.X + 0.5f; // Compute the weights of the pixel centers. var w0 = (BlurStrength > 0) ? MathHelper.Gaussian(o0, coefficient, 0, standardDeviation) : 0; var w1 = (BlurStrength > 0) ? MathHelper.Gaussian(o1, coefficient, 0, standardDeviation) : 0; _weights[i] = (w0 + w1); _weights[i + 1] = _weights[i]; weightSum += (_weights[i] * 2); // Shift the offset to the pixel center that has the higher weight. offset.X = (o0 * w0 + o1 * w1) / (w0 + w1); _horizontalOffsets[i] = offset / viewportSize.X; _horizontalOffsets[i + 1] = -_horizontalOffsets[i]; _verticalOffsets[i] = new Vector2(0, offset.X) / viewportSize.Y; _verticalOffsets[i + 1] = -_verticalOffsets[i]; } } else { // Same as above but: Sample in the middle of pixels. _horizontalOffsets[0] = new Vector2(0, 0); _verticalOffsets[0] = new Vector2(0, 0); _weights[0] = (BlurStrength > 0) ? MathHelper.Gaussian(0, coefficient, 0, standardDeviation) : 1; weightSum = _weights[0]; for (int i = 1; i < numberOfSamples; i += 2) { var offset = new Vector2(1 + i / 2, 0); _weights[i] = (BlurStrength > 0) ? MathHelper.Gaussian(offset.X, coefficient, 0, standardDeviation) : 0; _weights[i + 1] = _weights[i]; weightSum += (_weights[i] * 2); _horizontalOffsets[i] = offset / viewportSize.X; _horizontalOffsets[i + 1] = -_horizontalOffsets[i]; _verticalOffsets[i] = new Vector2(0, offset.X) / viewportSize.Y; _verticalOffsets[i + 1] = -_verticalOffsets[i]; } } // Normalize weights. for (int i = 0; i < numberOfSamples; i++) { _weights[i] /= weightSum; } }
private void DoScatteredInterpolation() { // Create random points for a height field. // The height field is in the x/z plane. Height is along the y axis. var points = new List <Vector3>(); for (int i = 0; i < 9; i++) { float x = i * 10; float y = RandomHelper.Random.NextFloat(0, 20); float z = RandomHelper.Random.NextInteger(0, 44) * 2; points.Add(new Vector3(x, y, z)); } // Now we setup scattered interpolation. // The RadialBasisRegression class does one form of scattered interpolation: // Multiple regression analysis with radial basis functions. RadialBasisRegressionF rbf = new RadialBasisRegressionF(); // We must define a basis function which will be used to determine the influence // of each input data pair. The Gaussian bell curve is a good candidate for most // applications. // We set the standard deviation to 10. Choosing a higher standard deviation makes // the Gaussian bell curve wider and increases the influence of the data points. // If we choose a lower standard deviation, we limit the influence of the data points. rbf.BasisFunction = (x, i) => MathHelper.Gaussian(x, 1, 0, 10); // Feed the data points into the scattered interpolation instance. foreach (var point in points) { // For each random point we add a data pair (X, Y), where X is a 2D position // in the height field, and Y is the observed height. VectorF X = new VectorF(new[] { point.X, point.Z }); VectorF Y = new VectorF(new[] { point.Y }); var dataPair = new Pair <VectorF, VectorF>(X, Y); rbf.Add(dataPair); } // These were all data points. Now, we perform some precomputations. rbf.Setup(); // Finally, we create a height field. var heightField = new float[100, 100]; for (int x = 0; x < 100; x++) { for (int z = 0; z < 100; z++) { // The scattered interpolation instance can compute a height for // any 2D input vector. float y = rbf.Compute(new VectorF(new float[] { x, z }))[0]; heightField[x, z] = y; } } var debugRenderer = GraphicsScreen.DebugRenderer; debugRenderer.Clear(); // Draw the random data points. const float scale = 0.04f; Vector3 offset = new Vector3(-2, 0, -2); foreach (var point in points) { debugRenderer.DrawPoint(scale * point + offset, Color.Black, false); } // Draw the height field. const int stepSize = 2; for (int x = 0; x < 100; x += stepSize) { for (int z = 0; z < 100; z += stepSize) { float y0 = heightField[x, z]; if (x + stepSize < 100) { float y1 = heightField[x + stepSize, z]; debugRenderer.DrawLine( scale * new Vector3(x, y0, z) + offset, scale * new Vector3(x + stepSize, y1, z) + offset, Color.Black, false); } if (z + stepSize < 100) { float y2 = heightField[x, z + stepSize]; debugRenderer.DrawLine( scale * new Vector3(x, y0, z) + offset, scale * new Vector3(x, y2, z + stepSize) + offset, Color.Black, false); } } } }
private void Stroke(FigureNode node, ArrayList<Vector3> strokeVertices, ArrayList<int> strokeIndices) { if (_mode != RenderMode.Stroke) { Flush(); _strokeEffect.CurrentTechnique.Passes[0].Apply(); _mode = RenderMode.Stroke; } // Use cached vertex buffer if available. var nodeRenderData = node.RenderData as FigureNodeRenderData; if (nodeRenderData != null && nodeRenderData.IsValid) { Flush(); var graphicsDevice = _graphicsService.GraphicsDevice; graphicsDevice.SetVertexBuffer(nodeRenderData.StrokeVertexBuffer); graphicsDevice.Indices = nodeRenderData.StrokeIndexBuffer; int primitiveCount = nodeRenderData.StrokeIndexBuffer.IndexCount / 3; graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, primitiveCount); #else int vertexCount = nodeRenderData.StrokeVertexBuffer.VertexCount; graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertexCount, 0, primitiveCount); return; } var batchVertices = _strokeBatch.Vertices; var world = node.PoseWorld * Matrix.CreateScale(node.ScaleWorld); var worldView = _view * world; var thickness = node.StrokeThickness; var color3F = node.StrokeColor * node.StrokeAlpha; var color = new HalfVector4(color3F.X, color3F.Y, color3F.Z, node.StrokeAlpha); var dash = node.StrokeDashPattern * node.StrokeThickness; bool usesDashPattern = (dash.Y + dash.Z) != 0; var dashSum = new HalfVector4( dash.X, dash.X + dash.Y, dash.X + dash.Y + dash.Z, dash.X + dash.Y + dash.Z + dash.W); // Convert to vertices. float lastDistance = 0; Vector3 lastPosition = new Vector3(float.NaN); Vector3 lastWorld = new Vector3(); Vector3 lastView = new Vector3(); Vector3 lastProjected = new Vector3(); var data0 = new HalfVector4(0, 1, thickness, 0); var data1 = new HalfVector4(0, 0, thickness, 0); var data2 = new HalfVector4(1, 0, thickness, 0); var data3 = new HalfVector4(1, 1, thickness, 0); Vector3[] figurePoints = strokeVertices.Array; int[] figureIndices = strokeIndices.Array; int numberOfLineSegments = strokeIndices.Count / 2; for (int i = 0; i < numberOfLineSegments; i++) { var startIndex = figureIndices[i * 2 + 0]; var endIndex = figureIndices[i * 2 + 1]; var start = figurePoints[startIndex]; var end = figurePoints[endIndex]; var notConnectedWithLast = start != lastPosition; lastPosition = end; Vector3 startWorld = notConnectedWithLast ? world.TransformPosition(start) : lastWorld; Vector3 endWorld = world.TransformPosition(end); lastWorld = endWorld; // Compute start/end distances of lines from beginning of line strip // for dash patterns. float startDistance = 0; float endDistance = 1; if (usesDashPattern) { if (!node.DashInWorldSpace) { Vector3 startView = notConnectedWithLast ? worldView.TransformPosition(start) : lastView; var endView = worldView.TransformPosition(end); lastView = endView; // Clip to near plane - otherwise lines which end near the camera origin // (where planar z == 0) will disappear. (Projection singularity!) float deltaZ = Math.Abs(startView.Z - endView.Z); float pStart = MathHelper.Clamp((startView.Z - (-_cameraNear)) / deltaZ, 0, 1); startView = InterpolationHelper.Lerp(startView, endView, pStart); float pEnd = MathHelper.Clamp((endView.Z - (-_cameraNear)) / deltaZ, 0, 1); endView = InterpolationHelper.Lerp(endView, startView, pEnd); Vector3 startProjected; if (notConnectedWithLast) { lastDistance = 0; startProjected = _viewport.ProjectToViewport(startView, _projection); } else { startProjected = lastProjected; } var endProjected = _viewport.ProjectToViewport(endView, _projection); lastProjected = endProjected; startDistance = lastDistance; endDistance = startDistance + (endProjected - startProjected).Length; lastDistance = endDistance; } else { if (notConnectedWithLast) lastDistance = 0; startDistance = lastDistance; endDistance = startDistance + (endWorld - startWorld).Length; lastDistance = endDistance; // The shader needs to know that DashInWorldSpace is true. To avoid // effect parameter changes, we store the value in the sign of the distance! startDistance = -startDistance; endDistance = -endDistance; } } var s = new Vector4(startWorld.X, startWorld.Y, startWorld.Z, startDistance); var e = new Vector4(endWorld.X, endWorld.Y, endWorld.Z, endDistance); int index, dummy; _strokeBatch.Submit(PrimitiveType.TriangleList, 4, 6, out index, out dummy); batchVertices[index + 0].Start = s; batchVertices[index + 0].End = e; batchVertices[index + 0].Data = data0; batchVertices[index + 0].Color = color; batchVertices[index + 0].Dash = dashSum; batchVertices[index + 1].Start = s; batchVertices[index + 1].End = e; batchVertices[index + 1].Data = data1; batchVertices[index + 1].Color = color; batchVertices[index + 1].Dash = dashSum; batchVertices[index + 2].Start = s; batchVertices[index + 2].End = e; batchVertices[index + 2].Data = data2; batchVertices[index + 2].Color = color; batchVertices[index + 2].Dash = dashSum; batchVertices[index + 3].Start = s; batchVertices[index + 3].End = e; batchVertices[index + 3].Data = data3; batchVertices[index + 3].Color = color; batchVertices[index + 3].Dash = dashSum; } }
private void CreateGuiControls() { var panel = SampleFramework.AddOptions("Shadows"); // ----- Light node controls var lightNodePanel = SampleHelper.AddGroupBox(panel, "Light Nodes"); SampleHelper.AddDropDown( lightNodePanel, "Light type", new[] { "Spotlight", "PointLight" }, 0, selectedItem => { bool enableSpotlight = (selectedItem == "Spotlight"); _spotlightNode.IsEnabled = enableSpotlight; _pointLightNode.IsEnabled = !enableSpotlight; }); SampleHelper.AddSlider( lightNodePanel, "Range", "F2", 1, 30, 10, value => { _spotlight.Range = value; _pointLight.Range = value; }); SampleHelper.AddSlider( lightNodePanel, "Y position", "F2", 0, 10, 1, value => { foreach (var node in new[] { _spotlightNode, _pointLightNode }) { var pose = node.PoseWorld; pose.Position.Y = value; node.PoseWorld = pose; } }); SampleHelper.AddSlider( lightNodePanel, "X rotation", "F0", -90, 90, 1, value => { var pose = _spotlightNode.PoseWorld; pose.Orientation = Matrix.CreateRotationX(MathHelper.ToRadians(value)); _spotlightNode.PoseWorld = pose; }); SampleHelper.AddSlider( lightNodePanel, "Spotlight angle", "F2", 1, 89, MathHelper.ToDegrees(_spotlight.CutoffAngle), value => { float angle = MathHelper.ToRadians(value); _spotlight.FalloffAngle = 0.8f * angle; _spotlight.CutoffAngle = angle; }); // ----- Shadow controls var shadowPanel = SampleHelper.AddGroupBox(panel, "Shadow"); SampleHelper.AddSlider( shadowPanel, "Shadow map resolution", "F0", 16, 1024, _standardShadow.PreferredSize, value => { _standardShadow.PreferredSize = (int)value; _cubeMapShadow.PreferredSize = (int)value; }); SampleHelper.AddCheckBox( shadowPanel, "Prefer 16 bit", _standardShadow.Prefer16Bit, isChecked => { _standardShadow.Prefer16Bit = isChecked; _cubeMapShadow.Prefer16Bit = isChecked; }); SampleHelper.AddSlider( shadowPanel, "Depth bias", "F2", 0, 10, _standardShadow.DepthBias, value => { _standardShadow.DepthBias = value; _cubeMapShadow.DepthBias = value; }); SampleHelper.AddSlider( shadowPanel, "Normal offset", "F2", 0, 10, _standardShadow.NormalOffset, value => { _standardShadow.NormalOffset = value; _cubeMapShadow.NormalOffset = value; }); SampleHelper.AddSlider( shadowPanel, "Number of samples", "F0", -1, 32, _standardShadow.NumberOfSamples, value => { _standardShadow.NumberOfSamples = (int)value; _cubeMapShadow.NumberOfSamples = (int)value; }); SampleHelper.AddSlider( shadowPanel, "Filter radius", "F2", 0, 10, _standardShadow.FilterRadius, value => { _standardShadow.FilterRadius = value; _cubeMapShadow.FilterRadius = value; }); SampleHelper.AddSlider( shadowPanel, "Jitter resolution", "F0", 1, 10000, _standardShadow.JitterResolution, value => { _standardShadow.JitterResolution = value; _cubeMapShadow.JitterResolution = value; }); SampleFramework.ShowOptionsWindow("Shadows"); }
public override void Render(IList <SceneNode> nodes, RenderContext context, RenderOrder order) { ThrowIfDisposed(); if (nodes == null) { throw new ArgumentNullException("nodes"); } if (context == null) { throw new ArgumentNullException("context"); } int numberOfNodes = nodes.Count; if (nodes.Count == 0) { return; } context.Validate(_effect); var originalRenderTarget = context.RenderTarget; var originalViewport = context.Viewport; var graphicsDevice = context.GraphicsService.GraphicsDevice; var savedRenderState = new RenderStateSnapshot(graphicsDevice); graphicsDevice.BlendState = BlendState.Opaque; graphicsDevice.RasterizerState = RasterizerState.CullNone; graphicsDevice.DepthStencilState = DepthStencilState.None; int frame = context.Frame; float deltaTime = (float)context.DeltaTime.TotalSeconds; for (int nodeIndex = 0; nodeIndex < numberOfNodes; nodeIndex++) { var cloudNode = nodes[nodeIndex] as CloudLayerNode; if (cloudNode == null) { continue; } var cloudMap = cloudNode.CloudMap as LayeredCloudMap; if (cloudMap == null) { continue; } // We update the cloud map only once per frame. if (cloudMap.LastFrame == frame) { continue; } cloudMap.LastFrame = frame; var layers = cloudMap.Layers; var animationTimes = cloudMap.AnimationTimes; var sources = cloudMap.SourceLayers; var targets = cloudMap.TargetLayers; var renderTargets = cloudMap.LayerTextures; // Animate the cloud map layers. for (int i = 0; i < LayeredCloudMap.NumberOfTextures; i++) { if (layers[i] == null || layers[i].Texture != null) { continue; } if (cloudMap.Random == null) { cloudMap.Random = new Random(cloudMap.Seed); } // Make sure there is a user-defined texture or data for procedural textures. if (sources[i] == null) { // Each octave is 128 x 128 (= 1 / 4 of the 512 * 512 noise texture). sources[i] = new PackedTexture(null, _noiseTexture, cloudMap.Random.NextVector2F(0, 1), new Vector2F(0.25f)); targets[i] = new PackedTexture(null, _noiseTexture, cloudMap.Random.NextVector2F(0, 1), new Vector2F(0.25f)); renderTargets[i] = new RenderTarget2D(graphicsDevice, 128, 128, false, SurfaceFormat.Alpha8, DepthFormat.None); } // Update animation time. animationTimes[i] += deltaTime * layers[i].AnimationSpeed; // Update source and target if animation time is beyond 1. if (animationTimes[i] > 1) { // Wrap animation time. animationTimes[i] = animationTimes[i] % 1; // Swap source and target. MathHelper.Swap(ref sources[i], ref targets[i]); // Set target to a new random part of the noise texture. targets[i].Offset = cloudMap.Random.NextVector2F(0, 1); } // Lerp source and target together to get the final noise texture. graphicsDevice.SetRenderTarget(renderTargets[i]); _parameterViewportSize.SetValue(new Vector2(graphicsDevice.Viewport.Width, graphicsDevice.Viewport.Height)); _parameterTextures[0].SetValue(sources[i].TextureAtlas); _parameterTextures[1].SetValue(targets[i].TextureAtlas); _parameterTexture0Parameters.SetValue(new Vector4(sources[i].Scale.X, sources[i].Scale.Y, sources[i].Offset.X, sources[i].Offset.Y)); _parameterTexture1Parameters.SetValue(new Vector4(targets[i].Scale.X, targets[i].Scale.Y, targets[i].Offset.X, targets[i].Offset.Y)); _parameterLerp.SetValue(animationTimes[i]); _passLerp.Apply(); graphicsDevice.DrawFullScreenQuad(); } // Initialize the cloud map. if (cloudMap.Texture == null || cloudMap.Size != cloudMap.Texture.Width) { cloudMap.Texture.SafeDispose(); var cloudTexture = new RenderTarget2D( graphicsDevice, cloudMap.Size, cloudMap.Size, false, SurfaceFormat.Alpha8, DepthFormat.None); cloudMap.SetTexture(cloudTexture); } // Combine the layers. graphicsDevice.SetRenderTarget((RenderTarget2D)cloudMap.Texture); _parameterViewportSize.SetValue(new Vector2(cloudMap.Texture.Width, cloudMap.Texture.Height)); for (int i = 0; i < LayeredCloudMap.NumberOfTextures; i++) { var layer = layers[i] ?? EmptyLayer; _parameterTextures[i].SetValue(layer.Texture ?? renderTargets[i]); _parameterMatrices[i].SetValue((Matrix) new Matrix44F(layer.TextureMatrix, Vector3F.Zero)); _parameterDensities[i].SetValue(new Vector2(layer.DensityScale, layer.DensityOffset)); } _parameterCoverage.SetValue(cloudMap.Coverage); _parameterDensity.SetValue(cloudMap.Density); _passDensity.Apply(); graphicsDevice.DrawFullScreenQuad(); } savedRenderState.Restore(); graphicsDevice.SetRenderTarget(null); context.RenderTarget = originalRenderTarget; context.Viewport = originalViewport; }
protected override void OnProcess(RenderContext context) { context.ThrowIfCameraMissing(); context.ThrowIfGBuffer0Missing(); var graphicsDevice = GraphicsService.GraphicsDevice; var renderTargetPool = GraphicsService.RenderTargetPool; var source = context.SourceTexture; var target = context.RenderTarget; var viewport = context.Viewport; // Get temporary render targets. var sourceSize = new Vector2F(source.Width, source.Height); var isFloatingPointFormat = TextureHelper.IsFloatingPointFormat(source.Format); var sceneFormat = new RenderTargetFormat(source.Width, source.Height, false, source.Format, DepthFormat.None); var maskedScene = renderTargetPool.Obtain2D(sceneFormat); var rayFormat = new RenderTargetFormat( Math.Max(1, (int)(sourceSize.X / DownsampleFactor)), Math.Max(1, (int)(sourceSize.Y / DownsampleFactor)), false, source.Format, DepthFormat.None); var rayImage0 = renderTargetPool.Obtain2D(rayFormat); var rayImage1 = renderTargetPool.Obtain2D(rayFormat); // Get view and view-projection transforms. var cameraNode = context.CameraNode; Matrix44F projection = cameraNode.Camera.Projection.ToMatrix44F(); Matrix44F view = cameraNode.View; Matrix44F viewProjection = projection * view; // We simply place the light source "far away" in opposite light ray direction. Vector4F lightPositionWorld = new Vector4F(-LightDirection * 10000, 1); // Convert to clip space. Vector4F lightPositionProj = viewProjection * lightPositionWorld; Vector3F lightPositionClip = Vector4F.HomogeneousDivide(lightPositionProj); // Convert from clip space [-1, 1] to texture space [0, 1]. Vector2 lightPosition = new Vector2(lightPositionClip.X * 0.5f + 0.5f, -lightPositionClip.Y * 0.5f + 0.5f); // We use dot²(forward, -LightDirection) as a smooth S-shaped attenuation // curve to reduce the god ray effect when we look orthogonal or away from the sun. var lightDirectionView = view.TransformDirection(LightDirection); float z = Math.Max(0, lightDirectionView.Z); float attenuation = z * z; // Common effect parameters. _parameters0Parameter.SetValue(new Vector4(lightPosition.X, lightPosition.Y, LightRadius * LightRadius, Scale)); _parameters1Parameter.SetValue(new Vector2(Softness, graphicsDevice.Viewport.AspectRatio)); _intensityParameter.SetValue((Vector3)Intensity * attenuation); _numberOfSamplesParameter.SetValue(NumberOfSamples); _gBuffer0Parameter.SetValue(context.GBuffer0); // First, create a scene image where occluders are black. graphicsDevice.SetRenderTarget(maskedScene); _viewportSizeParameter.SetValue(new Vector2(graphicsDevice.Viewport.Width, graphicsDevice.Viewport.Height)); _sourceTextureParameter.SetValue(source); graphicsDevice.SamplerStates[0] = isFloatingPointFormat ? SamplerState.PointClamp : SamplerState.LinearClamp; graphicsDevice.SamplerStates[1] = SamplerState.PointClamp; // G-Buffer 0. _createMaskPass.Apply(); graphicsDevice.DrawFullScreenQuad(); // Downsample image. context.SourceTexture = maskedScene; context.RenderTarget = rayImage0; context.Viewport = new Viewport(0, 0, rayImage0.Width, rayImage0.Height); _downsampleFilter.Process(context); // Compute light shafts. _viewportSizeParameter.SetValue(new Vector2(context.Viewport.Width, context.Viewport.Height)); graphicsDevice.SamplerStates[0] = isFloatingPointFormat ? SamplerState.PointClamp : SamplerState.LinearClamp; for (int i = 0; i < NumberOfPasses; i++) { graphicsDevice.SetRenderTarget(rayImage1); _sourceTextureParameter.SetValue(rayImage0); _blurPass.Apply(); graphicsDevice.DrawFullScreenQuad(); // Put the current result in variable rayImage0. MathHelper.Swap(ref rayImage0, ref rayImage1); } // Combine light shaft image with scene. graphicsDevice.SetRenderTarget(target); graphicsDevice.Viewport = viewport; _viewportSizeParameter.SetValue(new Vector2(graphicsDevice.Viewport.Width, graphicsDevice.Viewport.Height)); _sourceTextureParameter.SetValue(source); _rayTextureParameter.SetValue(rayImage0); graphicsDevice.SamplerStates[0] = isFloatingPointFormat ? SamplerState.PointClamp : SamplerState.LinearClamp; graphicsDevice.SamplerStates[1] = isFloatingPointFormat ? SamplerState.PointClamp : SamplerState.LinearClamp; _combinePass.Apply(); graphicsDevice.DrawFullScreenQuad(); // Clean-up _sourceTextureParameter.SetValue((Texture2D)null); _gBuffer0Parameter.SetValue((Texture2D)null); _rayTextureParameter.SetValue((Texture2D)null); renderTargetPool.Recycle(maskedScene); renderTargetPool.Recycle(rayImage0); renderTargetPool.Recycle(rayImage1); context.SourceTexture = source; context.RenderTarget = target; context.Viewport = viewport; }
/// <inheritdoc/> 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"); } if (order != RenderOrder.UserDefined) { throw new NotImplementedException("Render order must be 'UserDefined'."); } if (context.CameraNode == null) { throw new GraphicsException("Camera node needs to be set in render context."); } if (context.GBuffer0 == null) { throw new GraphicsException("GBuffer0 needs to be set in render context."); } int numberOfNodes = nodes.Count; if (numberOfNodes == 0) { return; } var graphicsService = context.GraphicsService; var graphicsDevice = graphicsService.GraphicsDevice; var viewport = context.Viewport; int width = viewport.Width; int height = viewport.Height; var renderTargetPool = graphicsService.RenderTargetPool; var cameraNode = context.CameraNode; var projection = cameraNode.Camera.Projection; Pose view = cameraNode.PoseWorld.Inverse; Pose cameraPose = cameraNode.PoseWorld; float near = projection.Near; float far = projection.Far; int frame = context.Frame; cameraNode.LastFrame = frame; // Save render state. var originalRasterizerState = graphicsDevice.RasterizerState; var originalDepthStencilState = graphicsDevice.DepthStencilState; var originalBlendState = graphicsDevice.BlendState; graphicsDevice.RasterizerState = RasterizerState.CullNone; graphicsDevice.DepthStencilState = DepthStencilState.None; RenderTarget2D offscreenBuffer = null; Texture depthBufferHalf = null; if (!EnableOffscreenRendering || context.RenderTarget == null) { graphicsDevice.BlendState = BlendState.AlphaBlend; _parameterGBuffer0.SetValue(context.GBuffer0); } else { // Render at half resolution into off-screen buffer. width = Math.Max(1, width / 2); height = Math.Max(1, height / 2); graphicsDevice.BlendState = BlendStateOffscreen; offscreenBuffer = renderTargetPool.Obtain2D( new RenderTargetFormat(width, height, false, context.RenderTarget.Format, DepthFormat.None)); graphicsDevice.SetRenderTarget(offscreenBuffer); graphicsDevice.Clear(Color.Black); // Get half-res depth buffer. object obj; if (context.Data.TryGetValue(RenderContextKeys.DepthBufferHalf, out obj) && obj is Texture2D) { depthBufferHalf = (Texture2D)obj; _parameterGBuffer0.SetValue(depthBufferHalf); } else { string message = "Downsampled depth buffer is not set in render context. (The downsampled " + "depth buffer (half width and height) is required by the VolumetricLightRenderer " + "to use half-res off-screen rendering. It needs to be stored in " + "RenderContext.Data[RenderContextKeys.DepthBufferHalf].)"; throw new GraphicsException(message); } } // Set global effect parameters. _parameterViewportSize.SetValue(new Vector2(width, height)); var isHdrEnabled = context.RenderTarget != null && context.RenderTarget.Format == SurfaceFormat.HdrBlendable; for (int i = 0; i < numberOfNodes; i++) { var node = nodes[i] as VolumetricLightNode; if (node == null) { continue; } // VolumetricLightNode is visible in current frame. node.LastFrame = frame; // Effect parameters for volumetric light properties. _parameterColor.SetValue((Vector3)node.Color / node.NumberOfSamples); _parameterNumberOfSamples.SetValue(node.NumberOfSamples); _parameterLightTextureMipMap.SetValue((float)node.MipMapBias); // The volumetric light effect is created for the parent light node. var lightNode = node.Parent as LightNode; if (lightNode == null) { continue; } Pose lightPose = lightNode.PoseWorld; // Get start and end depth values of light AABB in view space. var lightAabbView = lightNode.Shape.GetAabb(lightNode.ScaleWorld, view * lightPose); var startZ = Math.Max(-lightAabbView.Maximum.Z, near) / far; var endZ = Math.Min(-lightAabbView.Minimum.Z / far, 1); _parameterDepthInterval.SetValue(new Vector2(startZ, endZ)); // Get a rectangle that covers the light in screen space. var rectangle = GraphicsHelper.GetScissorRectangle(cameraNode, new Viewport(0, 0, width, height), lightNode); var texCoordTopLeft = new Vector2F(rectangle.Left / (float)width, rectangle.Top / (float)height); var texCoordBottomRight = new Vector2F(rectangle.Right / (float)width, rectangle.Bottom / (float)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); Vector2 randomSeed = AnimateNoise ? new Vector2((float)MathHelper.Frac(context.Time.TotalSeconds)) : new Vector2(0); _parameterRandomSeed.SetValue(randomSeed); // Set light parameters and apply effect pass. if (lightNode.Light is PointLight) { var light = (PointLight)lightNode.Light; float hdrScale = isHdrEnabled ? light.HdrScale : 1; _parameterLightDiffuse.SetValue((Vector3)light.Color * light.DiffuseIntensity * hdrScale); _parameterLightPosition.SetValue((Vector3)(lightPose.Position - cameraPose.Position)); _parameterLightRange.SetValue(light.Range); _parameterLightAttenuation.SetValue(light.Attenuation); bool hasTexture = (light.Texture != null); if (hasTexture) { _parameterLightTexture.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); _parameterLightTextureMatrix.SetValue((Matrix)(mirrorZ * lightPose.Inverse)); } if (hasTexture) { if (light.Texture.Format == SurfaceFormat.Alpha8) { _passPointLightTextureAlpha.Apply(); } else { _passPointLightTextureRgb.Apply(); } } else { _passPointLight.Apply(); } } else if (lightNode.Light is Spotlight) { var light = (Spotlight)lightNode.Light; float hdrScale = isHdrEnabled ? light.HdrScale : 1; _parameterLightDiffuse.SetValue((Vector3)light.Color * light.DiffuseIntensity * hdrScale); _parameterLightPosition.SetValue((Vector3)(lightPose.Position - cameraPose.Position)); _parameterLightRange.SetValue(light.Range); _parameterLightAttenuation.SetValue(light.Attenuation); _parameterLightDirection.SetValue((Vector3)lightPose.ToWorldDirection(Vector3.Forward)); _parameterLightAngles.SetValue(new Vector2(light.FalloffAngle, light.CutoffAngle)); bool hasTexture = (light.Texture != null); if (hasTexture) { _parameterLightTexture.SetValue(light.Texture); var proj = Matrix.CreatePerspectiveFieldOfView(light.CutoffAngle * 2, 1, 0.1f, 100); _parameterLightTextureMatrix.SetValue((Matrix)(GraphicsHelper.ProjectorBiasMatrix * proj * (lightPose.Inverse * new Pose(cameraPose.Position)))); } if (hasTexture) { if (light.Texture.Format == SurfaceFormat.Alpha8) { _passSpotlightTextureAlpha.Apply(); } else { _passSpotlightTextureRgb.Apply(); } } else { _passSpotlight.Apply(); } } else if (lightNode.Light is ProjectorLight) { var light = (ProjectorLight)lightNode.Light; float hdrScale = isHdrEnabled ? light.HdrScale : 1; _parameterLightDiffuse.SetValue((Vector3)light.Color * light.DiffuseIntensity * hdrScale); _parameterLightPosition.SetValue((Vector3)(lightPose.Position - cameraPose.Position)); _parameterLightRange.SetValue(light.Projection.Far); _parameterLightAttenuation.SetValue(light.Attenuation); _parameterLightTexture.SetValue(light.Texture); _parameterLightTextureMatrix.SetValue((Matrix)(GraphicsHelper.ProjectorBiasMatrix * light.Projection * (lightPose.Inverse * new Pose(cameraPose.Position)))); if (light.Texture.Format == SurfaceFormat.Alpha8) { _passProjectorLightTextureAlpha.Apply(); } else { _passProjectorLightTextureRgb.Apply(); } } else { continue; } // Draw a screen space quad covering the light. graphicsDevice.DrawQuad(rectangle); } _parameterGBuffer0.SetValue((Texture)null); _parameterLightTexture.SetValue((Texture)null); if (offscreenBuffer != null) { // ----- Combine off-screen buffer with scene. graphicsDevice.BlendState = BlendState.Opaque; // The previous scene render target is bound as texture. // --> Switch scene render targets! var sceneRenderTarget = context.RenderTarget; var renderTarget = renderTargetPool.Obtain2D(new RenderTargetFormat(sceneRenderTarget)); context.SourceTexture = offscreenBuffer; context.RenderTarget = renderTarget; // Use the UpsampleFilter, which supports "nearest-depth upsampling". // (Nearest-depth upsampling is an "edge-aware" method that tries to // maintain the original geometry and prevent blurred edges.) if (_upsampleFilter == null) { _upsampleFilter = new UpsampleFilter(graphicsService); _upsampleFilter.Mode = UpsamplingMode.NearestDepth; _upsampleFilter.RebuildZBuffer = true; } _upsampleFilter.DepthThreshold = DepthThreshold; context.SceneTexture = sceneRenderTarget; _upsampleFilter.Process(context); context.SceneTexture = null; context.SourceTexture = null; renderTargetPool.Recycle(offscreenBuffer); renderTargetPool.Recycle(sceneRenderTarget); } // Restore render states. graphicsDevice.RasterizerState = originalRasterizerState; graphicsDevice.DepthStencilState = originalDepthStencilState; graphicsDevice.BlendState = originalBlendState; }