private void OnCustomPlaneCheckBoxChanged(object sender, RoutedEventArgs e) { if (_planarShadowRenderingProvider != null) { if (CustomPlaneCheckBox.IsChecked ?? false) { // When we set ShadowPlaneMaterial and ShadowPlaneMaterial to null, then the 3D plane is not drawn any more. // But shadow can be still clipped to the bounds of the "plane" defined by the ShadowPlaneCenterPosition and ShadowPlaneSize. _planarShadowRenderingProvider.ShadowPlaneMaterial = null; _planarShadowRenderingProvider.ShadowPlaneBackMaterial = null; _wireGridVisual3D = new WireGridVisual3D() { CenterPosition = new Point3D(_planarShadowRenderingProvider.ShadowPlaneCenterPosition.X, _planarShadowRenderingProvider.ShadowPlaneCenterPosition.Y - 0.2, _planarShadowRenderingProvider.ShadowPlaneCenterPosition.Z), Size = new Size(_planarShadowRenderingProvider.ShadowPlaneSize.X, _planarShadowRenderingProvider.ShadowPlaneSize.Y), WidthCellsCount = 10, HeightCellsCount = 10, LineThickness = 1, }; MainViewport.Children.Add(_wireGridVisual3D); } else { _planarShadowRenderingProvider.ShadowPlaneMaterial = _shadowPlaneMaterial; _planarShadowRenderingProvider.ShadowPlaneBackMaterial = _shadowPlaneBackMaterial; if (_wireGridVisual3D != null) { MainViewport.Children.Remove(_wireGridVisual3D); _wireGridVisual3D = null; } } MainDXViewportView.Refresh(); } }
private void CreateTestScene() { // Line StartPosition and EndPosition define where the center of the line goes. // Half of line thickness is rendered on each side of the center line and the other half on the other side of the center line. // So if line thickness is 1 screen pixel and center of the line goes over the border between two pixels, // then line is rendered half pixels on each side of the center. // The doth (.) in the following image show where only half of the pixel is occupied by the line color. // If line color is black and background color is white, then such line will produce gray pixels // that will be shown as 2 pixels thick line. // Line with LineThickness = 1 pixel // StartPosition = (-1, 0) // EndPosition = (3, 0) // -2 -1 0 1 2 3 // 2 --------|------------ // | | | | | | // 1 --------|------------ // | | . | . | . | . | // 0 ----------|-------------- // | | . | . | . | . | // -1 --------|------------ // | | | | | | // -2 --------|------------ // To render a 1 pixel thick line in such a way that it will fill the whole pixel, // we need to move the center of the line so that it will go through the center of the pixels. // This will render the line as black line. // In the image below this is done with setting y to 0.5: // Line with LineThickness = 1 pixel // StartPosition = (-1, 0.5) // EndPosition = (3, 0.5) // -2 -1 0 1 2 3 // 2 --------|------------ // | | | | | | // 1 --------|------------ // | | x | x | x | x | // 0 ----------|-------------- // | | | | | | // -1 --------|------------ // | | | | | | // -2 --------|------------ // For this to work the following conditions need to be meet: // - the line need to be correctly positioned (also taking into account DPI settings and ZoomFactor) // - the camera need to be correctly set so that the (0, 0) position lies between screen pixels. // - DXViewportView need to be correctly snapped to screen pixels by WPF. // // It is not easy to correctly account all those parameters. // The current version of TwoDimensionalCamera does not yet support that. // // But with Ab3d.DXEngine it is possible to render some (or all) the lines // in such a way that they will be rendered exactly to screen pixels. // // This can be done with setting UseGeometryShaderFor3DLines and RenderAntialiased3DLines attributes to false. // This way the lines are rendered by DirectX. Such lines are always 1 screen pixel thick // and are always rendered to the screen pixels. // // The biggest disadvantage of this is that such lines are always 1 screen pixel thick. // // Such lines look good only when they are horizontal or vertical. // But when they are at an angle, the standard lines appear much nicer and smoother. // // Another disadvantage is that such lines are not rendered correctly when super-sampling is used. // The problem is that such line is rendered with 1 pixel thickness to a super-sized texture, // so when such texture is down-sampled the line will appear much dimmer because its color will // combined with surrounding colors. For example with 4xSSAA they will be half dimmer, with 16xSSAA they will be 4 times dimmer. // (standard lines are rendered thicker to super-sampled texture so thickness is correct after down-sampling). // We will render two sets of lines: // 1) The lines on the left will be rendered normally. // 2) The lines on the right will be rendered without using geometry shader and without anti-aliasing. double screenPixelLineThickness = _twoDimensionalCamera.ScreenPixelSize; var triangleFan1 = AddTriangleFan(new Point3D(-300, 60, 0), linesLength: 200, lineThickness: screenPixelLineThickness); var triangleFan2 = AddTriangleFan(new Point3D(100, 60, 0), linesLength: 200, lineThickness: screenPixelLineThickness); triangleFan2.SetDXAttribute(DXAttributeType.UseGeometryShaderFor3DLines, false); triangleFan2.SetDXAttribute(DXAttributeType.RenderAntialiased3DLines, false); var wireGridVisual1 = new WireGridVisual3D() { CenterPosition = new Point3D(-200, -60, 0), Size = new Size(200, 200), HeightDirection = new Vector3D(0, 1, 0), WidthDirection = new Vector3D(1, 0, 0), WidthCellsCount = 10, HeightCellsCount = 10, LineThickness = screenPixelLineThickness, }; MainViewport.Children.Add(wireGridVisual1); var wireGridVisual2 = new WireGridVisual3D() { CenterPosition = new Point3D(200, -60, 0), Size = new Size(200, 200), HeightDirection = new Vector3D(0, 1, 0), WidthDirection = new Vector3D(1, 0, 0), WidthCellsCount = 10, HeightCellsCount = 10, LineThickness = screenPixelLineThickness // NOTE: // When IsClosed is set to true or when using MajorLinesFrequency, // then WireGridVisual3D creates a Model3DGroup to render multiple line types. // In this case setting DXAttribute does not work. // If you want to to use MajorLinesFrequency, then create two WireGridVisual3D. // For closed WireGridVisual3D, create a separate RectangleVisual3D // MajorLinesFrequency = 2, // IsClosed = true }; wireGridVisual2.SetDXAttribute(DXAttributeType.UseGeometryShaderFor3DLines, false); wireGridVisual2.SetDXAttribute(DXAttributeType.RenderAntialiased3DLines, false); MainViewport.Children.Add(wireGridVisual2); // Add list of lines each with slightly different sub-pixel position. AddLineList(startPosition: new Point3D(-300, -220, 0), lineVector: new Vector3D(0, 40, 0), offsetVector: new Vector3D(10 + screenPixelLineThickness / 10, 0, 0), startLineThickness: screenPixelLineThickness, endLineThickness: screenPixelLineThickness, count: 20, renderAs1PixelNonAntiAliasedLine: false); AddLineList(startPosition: new Point3D(100, -220, 0), lineVector: new Vector3D(0, 40, 0), offsetVector: new Vector3D(10 + screenPixelLineThickness / 10, 0, 0), startLineThickness: screenPixelLineThickness, endLineThickness: screenPixelLineThickness, count: 20, renderAs1PixelNonAntiAliasedLine: true); // Add list of lines with different line thickness. // This shows that lines without geometry shader are always 1 screen pixel thick and do not support line thickness. AddLineList(startPosition: new Point3D(-300, -280, 0), lineVector: new Vector3D(0, 40, 0), offsetVector: new Vector3D(10 + screenPixelLineThickness / 10, 0, 0), startLineThickness: 0.5, endLineThickness: 5, count: 20, renderAs1PixelNonAntiAliasedLine: false); AddLineList(startPosition: new Point3D(100, -280, 0), lineVector: new Vector3D(0, 40, 0), offsetVector: new Vector3D(10 + screenPixelLineThickness / 10, 0, 0), startLineThickness: 0.5, endLineThickness: 5, count: 20, renderAs1PixelNonAntiAliasedLine: true); }
private void GenerateSampleObjects() { // We will show multiple instances of box MeshGeometry3D that is created here. // // IMPORTANT: // // It is very important to specify the correct position and size of the mesh. // The size should be one unit so that when you scale this mesh in instance data, // the scale factor will tell you the final size (for example xScale = 3 would render box with xSize set to 3). // // The position is even more important because when you will scale and rotate the mesh, // the mesh will be scaled and rotated around (0, 0, 0). // So if center of mesh is at (0, 0, 0), then the mesh will be also scale in all directions. // But if left edge is at (0, 0, 0), then the object will scale to the right (all coordinates are multiplied by the scale). // // What is more, the position that is at (0, 0, 0) in the original mesh will be positioned at the position // defined in the World transform by the M41, M42, M43 fields. // So if center of the mesh is at (0, 0, 0) - as in our case - then M41, M42, M43 will define the position of the center. // But if left-bottom-front edge would be at (0, 0, 0) - if we would use "centerPosition: new Point3D(0.5, 0.5, 0.5)" - // then M41, M42, M43 will define the position of the left-bottom-front edge when the box is shown. // // See also comments in CalculateMatrixFromPositionsAndSize method. Point3D boxCenterPosition = CenterBoxMeshToCoordinateCenter ? new Point3D(0, 0, 0) : new Point3D(0.5, 0, 0); var boxMeshGeometry3D = new Ab3d.Meshes.BoxMesh3D(centerPosition: boxCenterPosition, size: new Size3D(1, 1, 1), xSegments: 1, ySegments: 1, zSegments: 1).Geometry; // Uncomment the following line to see how an ArrowMesh3D would look like (note that arrow part is also scaled so ArrowMesh3D is not a very good candidate for mesh used for instancing when scale is not uniform) //boxMeshGeometry3D = new Ab3d.Meshes.ArrowMesh3D(startPosition: new Point3D(0, 0, 0), endPosition: new Point3D(1, 0, 0), radius: 0.1f, arrowRadius: 0.2f, arrowAngle: 45, segments: 10, generateTextureCoordinates: false).Geometry; // Create an instance of InstancedMeshGeometryVisual3D var instancedMeshGeometryVisual3D = new InstancedMeshGeometryVisual3D(boxMeshGeometry3D); // Create an array of InstanceData with the matrices define in the _matrices list and Green color. var instanceData = new InstanceData[_samplesData.Count]; for (int i = 0; i < _samplesData.Count; i++) { instanceData[i] = new InstanceData(_samplesData[i].Matrix, Colors.Green.ToColor4()); instanceData[i].World.M43 -= SamplesDistance * i; var wireGridVisual3D = new WireGridVisual3D() { CenterPosition = new Point3D(0, 0, -SamplesDistance * i), Size = new Size(4, 4), WidthCellsCount = 4, HeightCellsCount = 4, LineColor = Colors.Gray, LineThickness = 1 }; MainViewport.Children.Add(wireGridVisual3D); var yAxisLineVisual3D = new LineVisual3D() { StartPosition = wireGridVisual3D.CenterPosition, EndPosition = wireGridVisual3D.CenterPosition + new Vector3D(0, 1.2, 0), LineColor = wireGridVisual3D.LineColor, LineThickness = wireGridVisual3D.LineThickness, EndLineCap = LineCap.ArrowAnchor }; MainViewport.Children.Add(yAxisLineVisual3D); } instancedMeshGeometryVisual3D.InstancesData = instanceData; MainViewport.Children.Add(instancedMeshGeometryVisual3D); }
private void CreateSceneObjects() { // NOTE: // It is recommended to use meter as basic unit for all 3D objects used by VR. var rootVisual3D = new ModelVisual3D(); var wireGridVisual3D = new WireGridVisual3D() { CenterPosition = new Point3D(0, 0, 0), Size = new Size(3, 3), // 3 x 3 meters WidthCellsCount = 10, HeightCellsCount = 10, LineColor = Colors.Gray, LineThickness = 2 }; rootVisual3D.Children.Add(wireGridVisual3D); double centerX = 0; double centerZ = 0; double circleRadius = 1; var boxMaterial = new DiffuseMaterial(Brushes.Gray); var sphereMaterial = new MaterialGroup(); sphereMaterial.Children.Add(new DiffuseMaterial(Brushes.Silver)); sphereMaterial.Children.Add(new SpecularMaterial(Brushes.White, 16)); for (int a = 0; a < 360; a += 36) { double rad = SharpDX.MathUtil.DegreesToRadians(a); double x = Math.Sin(rad) * circleRadius + centerX; double z = Math.Cos(rad) * circleRadius + centerZ; var boxVisual3D = new BoxVisual3D() { CenterPosition = new Point3D(x, 0.3, z), Size = new Size3D(0.2, 0.6, 0.2), Material = boxMaterial }; var sphereVisual3D = new SphereVisual3D() { CenterPosition = new Point3D(x, 0.75, z), Radius = 0.15, Material = sphereMaterial }; rootVisual3D.Children.Add(boxVisual3D); rootVisual3D.Children.Add(sphereVisual3D); } MainViewport.Children.Clear(); MainViewport.Children.Add(rootVisual3D); Camera1.Heading = 25; Camera1.Attitude = -20; Camera1.Distance = 4; // 3 meters Camera1.Refresh(); }
private ModelVisual3D CreateTestObjects() { var modelVisual3D = new ModelVisual3D(); // NOTE: // The following box will be crated from 10 x 1 x 10 cells // This is needed to see at least some lights rendered on it in WPF rendering that is using per-vertex rendering // For DXEngine this is not needed because it is using per-pixel rendering (here we can create standard 1 x 1 x 1 box) var greenBaseBox = new BoxVisual3D() { CenterPosition = new Point3D(0, -5, 0), Size = new Size3D(2000, 10, 2000), XCellsCount = 20, YCellsCount = 1, ZCellsCount = 20, Material = new DiffuseMaterial(Brushes.White) }; modelVisual3D.Children.Add(greenBaseBox); var wireGridVisual3D = new WireGridVisual3D() { CenterPosition = new Point3D(0, 0.1, 0), Size = new Size(2000, 2000), WidthDirection = new Vector3D(1, 0, 0), HeightDirection = new Vector3D(0, 0, -1), WidthCellsCount = 100, HeightCellsCount = 100, MajorLinesFrequency = 5, IsClosed = true, MajorLineThickness = 1.5, MajorLineColor = Colors.DimGray, LineThickness = 0.8, LineColor = Colors.Gray, RenderingTechnique = WireGridVisual3D.WireGridRenderingTechniques.FixedMesh3DLines, // Use fixed MeshGeometry3D to render wire grid instead of dynamic 3D lines IsEmissiveMaterial = false // This also allows us to use standard material instead of emissive material - this means that the lines will be visible only where they are illuminated by the light. }; modelVisual3D.Children.Add(wireGridVisual3D); double centerX = 0; double centerZ = 0; var orangeBox = new BoxVisual3D() { CenterPosition = new Point3D(centerX, 100, centerZ), Size = new Size3D(50, 200, 50), Material = new DiffuseMaterial(Brushes.Orange) }; modelVisual3D.Children.Add(orangeBox); var blueSpecularMaterial = new MaterialGroup(); blueSpecularMaterial.Children.Add(new DiffuseMaterial(Brushes.LightSkyBlue)); blueSpecularMaterial.Children.Add(new SpecularMaterial(Brushes.White, 20)); for (int a = 0; a < 360; a += 40) { double rad = SharpDX.MathUtil.DegreesToRadians(a); double x = Math.Sin(rad) * 100 + centerX; double z = Math.Cos(rad) * 100 + centerZ; var sphereVisual3D = new SphereVisual3D() { CenterPosition = new Point3D(x, 25, z), Radius = 25, Segments = 10, // Intentially lowered from default 30 to make the rendering difference bigger (between per vertex (WPF) and per pixel (DXEngine) rendering) Material = blueSpecularMaterial }; modelVisual3D.Children.Add(sphereVisual3D); } return(modelVisual3D); }