private bool worldMatrixDirty = true; // set to true if worldMatrix is no longer valid, see 'UpdateWorldMatrix()' //create the actor public Actor(ContentRegister content, UpdateManager updateManager, MaterialLightCollection lights, float groundRadius) { this.groundRadius = groundRadius; model = new ModelInstance(); model.LightCollection = lights; //force the model to render using spherical harmonic lighting //this will significantly improve performance on some hardware when GPU limited model.ShaderProvider = new Xen.Ex.Graphics.Provider.LightingDisplayShaderProvider(LightingDisplayModel.ForceSphericalHarmonic, model.ShaderProvider); //random starting position position = GetRandomPosition(); //initially target the current position //the 'move to next target' timer will wind down and then the actor will move target = position; //randomize a bit lookAngle = (float)random.NextDouble() * MathHelper.TwoPi; moveSpeed = (float)random.NextDouble() * 0.9f + 0.1f; content.Add(this); updateManager.Add(this); InitaliseAnimations(updateManager); }
//constructor public SphereDrawer(Vector3 position) { //setup the sphere geometry var size = new Vector3(1,1,1); //Use the prebuilt sphere geometry class this.sphereGeometry = new Sphere(size, 32); //Setup the world matrix this.worldMatrix = Matrix.CreateTranslation(position); //Create a lighting shader with some nice looking lighting //'MaterialShader' is a prebuilt class in Xen.Ex. It is similar to the XNA BasicEffect //This class implements the IShader interface. All Xen shaders implement IShader. this.shader = new MaterialShader(); this.shader.SpecularColour = Color.LightYellow.ToVector3(); //give the material a nice sheen var lightDirection = new Vector3(0.5f,1,-0.5f); //a dramatic direction //create a light collection and add a couple of lights to it var lights = new MaterialLightCollection(); lights.AmbientLightColour = Color.CornflowerBlue.ToVector3() * 0.5f; //set the ambient lights.CreateDirectionalLight(lightDirection, Color.Gray); //add the first of two light sources lights.CreateDirectionalLight(-lightDirection, Color.DarkSlateBlue); //set the light collection used by the material shader this.shader.LightCollection = lights; }
public ObjectComposer AppendMaterialLights(MaterialLightCollection collection = null) { if (currentObject.Shader == null) throw new Exception("Append shader before customising it!"); if(collection == null) collection = new MaterialLightCollection(); currentObject.Shader.LightCollection = collection; return this; }
public GroundDisk(ContentRegister content, MaterialLightCollection lights, float radius) { this.radius = radius; int vertexCount = 256; var indices = new List<int>(); //create the vertices. Note the DiskVertex() constructor takes an angle/size var verts = new DiskVertex[vertexCount]; for (int i = 0; i < vertexCount; i++) { verts[i] = new DiskVertex((i / (float)(vertexCount - 1)) * MathHelper.TwoPi, radius, 0.05f); if (i != 0) //add the tirangle indices { indices.Add(0); indices.Add(i - 1); indices.Add(i); } } //create the vertex buffer this.vertices = new Vertices<DiskVertex>(verts); this.indices = new Indices<int>(indices); //create the custom material for this geometry //the light collection has been passed into the constructor, although it //could easily be changed later (by changing material.Lights) this.Material = new MaterialShader(lights); //give the disk really bright specular for effect Material.SpecularColour = new Vector3(1,1,1); Material.DiffuseColour = new Vector3(0.6f, 0.6f, 0.6f); Material.SpecularPower = 64; //setup the texture samples to use high quality anisotropic filtering //the textures are assigned in LoadContent Material.Textures = new MaterialTextures(); Material.Textures.TextureMapSampler = TextureSamplerState.AnisotropicHighFiltering; Material.Textures.NormalMapSampler = TextureSamplerState.AnisotropicLowFiltering; //load the textures for this material content.Add(this); }
public GroundDisk(ContentRegister content, float radius, MaterialLightCollection lights) { //build the disk VertexPositionNormalTexture[] vertexData = new VertexPositionNormalTexture[256]; for (int i = 0; i < vertexData.Length; i++) { //a bunch of vertices, in a circle! float angle = (float)i / (float)vertexData.Length * MathHelper.TwoPi; Vector3 position = new Vector3((float)Math.Sin(angle), (float)Math.Cos(angle), 0); vertexData[i] = new VertexPositionNormalTexture(position * radius, new Vector3(0, 0, 1), new Vector2(position.X, position.Y)); } this.vertices = new Vertices <VertexPositionNormalTexture>(vertexData); //create the material, and add to content this.material = new MaterialShader(); this.material.Lights = lights; content.Add(this); }
//NEW CODE public Actor(ContentRegister content, MaterialLightCollection lights) { //A ModelInstance can be created without any content... //However it cannot be used until the content is set model = new ModelInstance(); model.LightCollection = lights; //this class is reused by later tutorials, which require lights //get and create the animation controller for this model. animationController = model.GetAnimationController(); //NOTE: Animations cannot be played until the model data has been loaded... content.Add(this); //At this point in this tutorial, the model is now loaded. //get the index of the walk animation //this model has 4 animations, Wave, Jog, Walk and Loiter //The animations are stored in model.ModelData.Animations int animationIndex = animationController.AnimationIndex("Walk"); //begin playing the animation, looping animation = animationController.PlayLoopingAnimation(animationIndex); //as many animations as you want can be played at any one time //to blend between animations, adjust their weighting with: //animation.Weighting = ...; //Combined weightings usually should add up to 1.0 //A weighting of 0 means the animation has no effect, 1 has normal effect. //Values outside the 0-1 range usually produces undesirable results. //Note: //Animations in xen are lossy compressed. //For the model used here, the animation data is reduced from nearly 2mb //down to around 200kb. (The model geometry is less than 300kb) //The amount of compression change can be configured in the content's properties //The 'Animation Compression Tolerance' value is a percentage //The default is .5%. This means the animation will always be within .5% //of the source. Setting this value to 0 will save a lossless animation. }
public GroundDisk(ContentRegister content, MaterialLightCollection lights, float radius) { this.radius = radius; int vertexCount = 256; //create the vertices. Note the DiskVertex() constructor takes an angle/size DiskVertex[] verts = new DiskVertex[vertexCount]; for (int i = 0; i < vertexCount; i++) { verts[i] = new DiskVertex((i / (float)(vertexCount - 1)) * MathHelper.TwoPi, radius, 0.05f); } //create the vertex buffer this.vertices = new Vertices <DiskVertex>(verts); //create the custom material for this geometry //the light collection has been passed into the constructor, although it //could easily be changed later (by changing material.Lights) this.material = new MaterialShader(lights); //By default, per-pixel lighting in the material shader does not do //specular reflection. This is because specular nearly triples the //complexity of the lighting calculation - which makes rendering slower //and reduces the maximum number of per-pixel lights supported from 4 to 2. material.UsePerPixelSpecular = true; //give the disk really bright specular for effect material.SpecularColour = new Vector3(1, 1, 1); material.DiffuseColour = new Vector3(0.6f, 0.6f, 0.6f); material.SpecularPower = 64; //setup the texture samples to use high quality anisotropic filtering material.TextureMapSampler = TextureSamplerState.AnisotropicHighFiltering; material.NormalMapSampler = TextureSamplerState.AnisotropicLowFiltering; //load the textures for this material content.Add(this); }
private bool worldMatrixDirty = true; // set to true if worldMatrix is no longer valid, see 'UpdateWorldMatrix()' //create the actor public Actor(ContentRegister content, UpdateManager updateManager, MaterialLightCollection lights, float groundRadius) { this.groundRadius = groundRadius; model = new ModelInstance(); model.LightCollection = lights; //random starting position position = GetRandomPosition(); //initially target the current position //the 'move to next target' timer will wind down and then the actor will move target = position; //randomize a bit lookAngle = (float)random.NextDouble() * MathHelper.TwoPi; moveSpeed = (float)random.NextDouble() * 0.9f + 0.1f; content.Add(this); updateManager.Add(this); InitaliseAnimations(updateManager); }
protected override void Initialise() { camera = new Camera3D(); //create the draw target. drawToScreen = new DrawTargetScreen(this, camera); //clear to dark blue drawToScreen.ClearBuffer.ClearColour = new Color(20, 20, 40); //create the light collection lights = new MaterialLightCollection(); //set a dark blue ambient colour lights.AmbientLightColour = new Color(40, 40, 80).ToVector3(); //positions for two lights Vector3[] lightPositions = new Vector3[] { new Vector3(0, 30, 2), new Vector3(0, -30, 2) }; //geometry for a light (shared for each light) IDraw lightGeometry = null; for (int i = 0; i < lightPositions.Length; i++) { float lightHalfFalloffDistance = 15; Color lightColor = Color.LightYellow; Color lightSpecularColour = Color.WhiteSmoke; bool perPixel = i < 2; //first two lights are per-pixel //interface to the light about to be created IMaterialPointLight light = null; //create the point light light = lights.AddPointLight(perPixel, lightPositions[i], lightHalfFalloffDistance, lightColor, lightSpecularColour); //adjust the lighting attenuation model, the constant defaults to 1, which prevents the light being brighter than 1.0 in the falloff equation //set to 0.25, the light will get really bright in close (up to 4) //(see light.QuadraticAttenuation remarks for an explanation of the falloff model) light.ConstantAttenuation = 0.25f; //create the light geometry (a sphere) if (lightGeometry == null) { lightGeometry = new Xen.Ex.Geometry.Sphere(Vector3.One, 8, true, false, false); } //visually show the light with a light drawer IDraw lightSourceDrawer = new LightSourceDrawer(lightPositions[i], lightGeometry, lightColor); //add the light geometry to the screen drawToScreen.Add(lightSourceDrawer); } //create the ground disk GroundDisk ground = new GroundDisk(this.Content, lights, diskRadius); //then add it to the screen drawToScreen.Add(ground); }
/// <summary> /// Draw the model. This class automatically assigns shaders when drawing /// </summary> /// <param name="state"></param> public void Draw(DrawState state) { if (modelData == null) { throw new InvalidOperationException("ModelData is null"); } if (controller != null) { controller.WaitForAsyncAnimation(state, state.FrameIndex, true); if (controller.IsDisposed) { controller = null; } } if (controller != null && hierarchy == null) { hierarchy = new MaterialAnimationTransformHierarchy(modelData.skeleton); } if (hierarchy != null) { hierarchy.UpdateTransformHierarchy(controller.transformedBones); } ModelInstanceShaderProvider shaderProvider = this.shaderProvider; MaterialLightCollection lights = this.lights; ShaderProviderFlag providerFlag; MaterialLightCollection.LightCollectionFlag lightsFlag; state.GetDrawFlag(out providerFlag); if (providerFlag.OverrideShaderProvider) { shaderProvider = providerFlag.ShaderProvider; } state.GetDrawFlag(out lightsFlag); if (lightsFlag.OverrideLightCollection) { lights = lightsFlag.LightCollection; } if (shaderProvider != null) { if (controller != null) { shaderProvider.BeginDraw(state, controller.transformedBones, hierarchy.GetMatrixData()); } else { shaderProvider.BeginDraw(state); } } Vector3 boundsMin, boundsMax; ContainmentType cullModel = ContainmentType.Contains; //if there is just one geometry object, then the ICullable.CullTest() call will have been suficient. bool skipCullTest = this.modelData != null && this.modelData.meshes.Length == 1 && this.modelData.meshes[0].geometry.Length == 1; if (!skipCullTest) { if (controller != null) { cullModel = state.Culler.IntersectBox(ref controller.boundsMin, ref controller.boundsMax); } else { cullModel = state.Culler.IntersectBox(ref modelData.staticBounds.minimum, ref modelData.staticBounds.maximum); } } if (cullModel != ContainmentType.Disjoint) { for (int m = 0; m < modelData.meshes.Length; m++) { MeshData mesh = modelData.meshes[m]; if (shaderProvider != null) { shaderProvider.BeginMesh(state, mesh); } ContainmentType cullMesh = cullModel; if (cullModel == ContainmentType.Intersects && modelData.meshes.Length > 1) { if (controller != null) { controller.ComputeMeshBounds(m, out boundsMin, out boundsMax); cullMesh = state.Culler.IntersectBox(ref boundsMin, ref boundsMax); } else { cullMesh = state.Culler.IntersectBox(ref mesh.staticBounds.minimum, ref mesh.staticBounds.maximum); } } if (cullMesh != ContainmentType.Disjoint) { for (int g = 0; g < mesh.geometry.Length; g++) { GeometryData geom = mesh.geometry[g]; MaterialShader shader = geom.MaterialShader; if (shaderProvider != null && shaderProvider.BeginGeometryShaderOverride(state, geom, lights)) { shader = null; } bool cullTest = true; if (cullMesh == ContainmentType.Intersects && mesh.geometry.Length > 1) { if (controller != null) { controller.ComputeGeometryBounds(m, g, out boundsMin, out boundsMax); cullTest = state.Culler.TestBox(ref boundsMin, ref boundsMax); } else { cullTest = state.Culler.TestBox(ref geom.staticBounds.minimum, ref geom.staticBounds.maximum); } } if (cullTest) { if (shader != null) { shader.AnimationTransforms = hierarchy; shader.Lights = lights; shader.Bind(state); } geom.Vertices.Draw(state, geom.Indices, PrimitiveType.TriangleList); } if (shaderProvider != null) { shaderProvider.EndGeometry(state, geom); } } } if (shaderProvider != null) { shaderProvider.EndMesh(state, mesh); } } } if (shaderProvider != null) { shaderProvider.EndDraw(state); } }
/// <summary> /// <para>Called before drawing geometry</para> /// <para>Return true if the shader has been overridden</para> /// </summary> /// <param name="geometry"></param> /// <param name="lights"></param> /// <returns></returns> /// <remarks>If pushing the world matrix, make sure to pop it in <see cref="EndGeometry"/></remarks> /// <param name="state"></param> public virtual bool BeginGeometryShaderOverride(DrawState state, GeometryData geometry, MaterialLightCollection lights) { return(false); }
protected override void Initialise() { //setup ambient lighting this.ambientLight = new MaterialLightCollection(); ambientLight.LightingEnabled = true; ambientLight.AmbientLightColour = new Vector3(0.4f, 0.2f, 0.1f); ambientLight.CreateDirectionalLight(new Vector3(-1, -1, 0), new Vector3(3,2,1)); // add some backlighting ambientLight.SphericalHarmonic.AddLight(new Vector3(2, 0.5f, 0.25f), new Vector3(0, 0, 1), 0.2f); //the camera for the shadows point of view, represents the direction of the light. Camera3D shadowCamera = new Camera3D(); shadowCamera.LookAt(new Vector3(1, 1, 3), new Vector3(-15, 20, 20), new Vector3(0, 0, 1)); //set the clip plane distances shadowCamera.Projection.FarClip = 40; shadowCamera.Projection.NearClip = 20; shadowCamera.Projection.FieldOfView *= 0.25f; //8bit is actually enough accuracy for this sample (given the limited range of the shadow) var textureFormat = SurfaceFormat.Color; const int resolution = 256; //create the shadow map texture: drawShadowDepth = new DrawTargetTexture2D(shadowCamera, resolution, resolution, textureFormat, DepthFormat.Depth24); drawShadowDepth.ClearBuffer.ClearColour = Color.White; //for the shadow technique used, the shadow buffer is blurred. //this requires an intermediate render target on the PC DrawTargetTexture2D blurIntermediate = null; //technically not required on the xbox if the render target is small enough to fit in EDRAM in one tile, but xna insists blurIntermediate = new DrawTargetTexture2D(shadowCamera, resolution, resolution, textureFormat, DepthFormat.None); //create a blur filter shadowDepthBlurFilter = new Xen.Ex.Filters.BlurFilter(Xen.Ex.Filters.BlurFilterFormat.SevenSampleBlur,1.0f, drawShadowDepth, blurIntermediate); //create the scene camera var camera = new Camera3D(); camera.LookAt(new Vector3(0, 0, 3), new Vector3(10, 10, 6), new Vector3(0, 0, 1)); camera.Projection.FieldOfView *= 0.55f; //create the draw target. drawToScreen = new DrawTargetScreen(camera); drawToScreen.ClearBuffer.ClearColour = Color.Black; //the 'scene' //A DrawList from Tutorial 23 is used here, this stores the 'scene', //which is just a set of actors and the ground Tutorials.Tutorial_23.DrawList scene = new Tutorials.Tutorial_23.DrawList(); for (int x = 0; x < 2; x++) for (int y = 0; y < 2; y++) { //create the actor instances if (x != 0 || y != 0) scene.Add(new Actor(this.Content, new Vector3(x*6-3, y*6-3, 0), (x + y*2 + 1) * 0.2f, 4-x*2-y)); } //add the ground var ground = new GroundDisk(this.Content, 10, ambientLight); scene.Add(ground); //setup the draw targets... //create the shader provider var shadowOutputShaderProvider = new ShadowOutputShaderProvider(); //add a ShadowMapDrawer to the shadow map texture drawShadowDepth.Add(new ShadowMapDrawer(scene, shadowOutputShaderProvider)); //setup the scene to be drawn to the screen //draw the scene normally (no shadow, just ambient) drawToScreen.Add(scene); Vector3 lightColour = new Vector3(2, 1.5f, 1); //then draw the scene with a shadow (blended on top) drawToScreen.Add(new ShadowedSceneDrawer(scene, shadowOutputShaderProvider, drawShadowDepth, lightColour)); //add a nice faded background Tutorial_20.BackgroundGradient background = new Tutorial_20.BackgroundGradient(new Color(1, 0.5f, 0.3f), new Color(0.2f, 0.1f, 0.2f)); background.DrawAtMaxZDepth = true; drawToScreen.Add(background); //create a textured element that will display the shadow map texture var shadowDepthDisplay = new TexturedElement(drawShadowDepth, new Vector2(256, 256)); shadowDepthDisplay.VerticalAlignment = VerticalAlignment.Top; this.drawToScreen.Add(shadowDepthDisplay); }
public GroundDisk(ContentRegister content, float radius, MaterialLightCollection lights) { //build the disk var vertexData = new VertexPositionNormalTexture[256]; var indices = new List<int>(); for (int i = 1; i < vertexData.Length; i++) { //a bunch of vertices, in a circle! float angle = (float)(i-1) / (float)(vertexData.Length-2) * MathHelper.TwoPi; Vector3 position = new Vector3((float)Math.Sin(angle), (float)Math.Cos(angle), 0); vertexData[i] = new VertexPositionNormalTexture(position * radius, new Vector3(0, 0, 1), new Vector2(position.X, position.Y)); if (i > 1) { indices.Add(0); indices.Add(i - 1); indices.Add(i); } } vertexData[0] = new VertexPositionNormalTexture(new Vector3(), new Vector3(0, 0, 1), new Vector2()); this.vertices = new Vertices<VertexPositionNormalTexture>(vertexData); this.indices = new Indices<int>(indices); //create the material, and add to content this.material = new MaterialShader(); this.material.LightCollection = lights; this.material.Textures = new MaterialTextures(); content.Add(this); }
//no change to the shader: public IShader BeginModel(DrawState state, MaterialLightCollection lights) { return null; }
/// <summary> /// Draw all the model batch instances /// </summary> /// <param name="state"></param> public void Draw(DrawState state) { if (modelData == null) throw new InvalidOperationException("ModelData is null"); if (buffers == null) SetupBuffers(); int bufferIndex = 0; IModelShaderProvider shaderProvider = this.shaderProvider; MaterialLightCollection lights = this.lights; ModelShaderProviderFlag providerFlag; MaterialLightCollectionFlag lightsFlag; Xen.Graphics.Stack.DrawFlagStack flags = state.DrawFlags; flags.GetFlag(out providerFlag); if (providerFlag.OverrideShaderProvider) shaderProvider = providerFlag.ShaderProvider; flags.GetFlag(out lightsFlag); if (lightsFlag.OverrideLightCollection) lights = lightsFlag.LightCollection; IShader shader = null; if (shaderProvider != null) { shader = shaderProvider.BeginModel(state, lights); if (shader != null) state.Shader.Push(shader); } //loop through the model data for (int m = 0; m < modelData.meshes.Length; m++) { MeshData mesh = modelData.meshes[m]; for (int g = 0; g < mesh.geometry.Length; g++) { GeometryData geom = mesh.geometry[g]; InstanceBuffer buffer = this.buffers[bufferIndex]; if (buffer != null) { IShader geomShader = null; if (shaderProvider != null) { geomShader = shaderProvider.BeginGeometry(state, geom); if (geomShader != null) state.Shader.Push(geomShader); } //draw the geometry geom.Vertices.DrawInstances(state, geom.Indices, PrimitiveType.TriangleList, buffer); if (geomShader != null) state.Shader.Pop(); } this.buffers[bufferIndex] = null; bufferIndex++; } } if (shaderProvider != null) { shaderProvider.EndModel(state); if (shader != null) state.Shader.Pop(); } drawCount = 0; }
protected override void Initialise() { camera = new Camera3D(); camera.Projection.FarClip = 300; camera.Projection.NearClip = 10; camera.Projection.FieldOfView *= 0.55f; //create the draw target. drawToScreen = new DrawTargetScreen(camera); //no need to clear the colour buffer, as a special background will be drawn drawToScreen.ClearBuffer.ClearColourEnabled = false; //create the light collection first lights = new MaterialLightCollection(); // In this example, the rendering order has been manually optimized to reduce the number of pixels drawn // // In xen, rendering is usually explicit. This means, when a call to Draw() is made, the draw order is // respected, and internally the object will be drawn using the graphics API. // Objects added to the screen will have Draw() called in the order they were added. // // However, the draw order can also cause performance problems. // In general, it's best to draw front to back, this means draw the objects closest to the screen first. // // This way, the objects at the back will be drawing behind the objects already drawn. // Modern video cards can quickly discard pixels if they are 'behind' what is already drawn. // Without front-to-back, the objects at the front could be drawing *over* objects already drawn. // // This is known as overdraw, a case where an object is drawn, only to be 'overdrawn' later in the frame. // Reducing overdraw can help performance, especially when complex shaders are used. // // In this example, the sample is usually vertex-limited (that is, the bottleneck is vertex processing) // However, it can demonstrate how optimizing for overdraw can significantly reduce the number of pixels // that are shaded. // // In debug builds, the DrawStatisticsDisplay class will show the number of pixels drawn in the frame. // // With overdraw optimized draw order, ~1,000,000 pixels are drawn per frame. Without, upto 2,100,000 // pixels are drawn per frame (usually ~1,800,000). (A 1280x720 display has 921,600 pixels) // // This means that without an overdraw optimized draw order, on average, each pixel is being drawn // twice. With an optimized draw order, this number is closer to 1.1, which is very close to the // optimal value of 1.0 (where each pixel is only drawn once). // // Note that the number of pixels reported by the DrawStatisticsDisplay is for the entire frame, including // every render target. Some PCs may not support this value, and display -1. // // One last point.... // This sample is an extreme test of a GPU's ability to push triangles onto the screen (vertex/triangle rate). // However, observation will show the number of triangles drawn is often over 3,300,000! // Assuming half the triangles are back-face culled (an accurate approximation), this still means // there are around 1,650,000 triangles that are visible at any time. // (But remember, the vertex shader still runs for back facing triangles!) // // Assuming approximatly half of these triangles are depth occluded (very approximate), still // results in a huge number of visible triangles. // This all means that the average triangle is drawing a *very* small number of pixels, in this case, // the average for the actors is probably *less than 1 pixel per triangle!*. // // Triangles averaging less than 1 pixel are known as subpixel triangles. // For a number of reasons, subpixel triangles are very inefficent. // For example, if a single pixel is drawn, due to the way a video card works, the pixel shader will always // run in multiples of 4 pixels, so a single pixel triangle will still run the pixel shader 4 times. // // As an approximate rule: // Typically drawing a 1 pixel triangle will be as no faster than drawing a 16 pixel triangle. // // This makes this sample a perfect candidate for level of detail optimization, where a lower resolution // model is used as an actor gets further away from the screen. (Eg, two modelInstances, sharing a controller) // // The vertex shader is also very expensive, and for each triangle, it will be run upto 3 times. // This means the vertex shader is running more often than the pixel shader! // This hypothesis can be confirmed; setting the lights to per-vertex, instead of per-pixel, results // in a significantly *lower* frame rate! // // // bool optimizeForOverdraw = true; //create a list of actors to added to the screen var actors = new List<Actor>(500); //create 500 actors! for (int i = 0; i < 500; i++) { Actor actor = new Actor(this.Content, this.UpdateManager, lights, diskRadius); actors.Add(actor); } //create the lights, similar to Tutorial 14 lights.AmbientLightColour = new Vector3(0.35f, 0.35f, 0.45f); Vector3[] lightPositions = { new Vector3(0, 30, 12), new Vector3(0, -30, 12) }; //setup the two lights in the scene IDraw lightGeometry = null; IDraw lightPoleGeometry = null; //create geometry to display the lights var lightSourceGeometry = new List<IDraw>(); //setup the lights, and create the light globe geometry for (int i = 0; i < lightPositions.Length; i++) { var colour = new Vector3(2, 2, 2); var light = lights.CreatePointLight(lightPositions[i], 1, colour, colour); light.SourceRadius = 6; if (lightGeometry == null) { lightGeometry = new Xen.Ex.Geometry.Sphere(Vector3.One, 8, true, false, false); lightPoleGeometry = new Xen.Ex.Geometry.Cube(new Vector3(0.4f, 0.4f, lightPositions[i].Z * 0.5f)); } //visually show the light //create the light sphere geometry from tutorial 14. var position = lightPositions[i]; lightSourceGeometry.Add(new Tutorial_14.LightSourceDrawer(position, lightGeometry, Color.LightYellow)); position.Z *= 0.5f; lightSourceGeometry.Add(new Tutorial_14.LightSourceDrawer(position, lightPoleGeometry, new Color(40,40,70))); } //create the ground plane, also from tutorial 14 var ground = new Tutorial_14.GroundDisk(this.Content, lights, diskRadius); //this is a special background element, //it draws a gradient over the entire screen, fading from dark at the bottom to light at the top. Color darkBlue = new Color(40, 40, 50); Color lightBlue = new Color(100, 100, 110); var background = new BackgroundGradient(lightBlue, darkBlue); if (optimizeForOverdraw == false) { //add all the objects in a naive order //first add the background (fills the entire screen, draws to every pixel, but is very fast) drawToScreen.Add(background); //then add the ground plane (all the actors will appear on top of the ground plane, overdrawing it) drawToScreen.Add(ground); //then add the lights (which are on top of the ground, overdrawing it) foreach (IDraw geometry in lightSourceGeometry) drawToScreen.Add(geometry); //then finally add the actors, in the order they were created foreach (Actor actor in actors) drawToScreen.Add(actor); } else { //or, add the objects in a order optimized for overdraw #if !XBOX360 //first, add the actors. Because they are almost always closest to the screen //however, use a depth sorter so the actors are sorted into a front to back draw order, //this sorting is based on the centre point of the cull tests they perform. var sorter = new Xen.Ex.Scene.DepthDrawSorter(Xen.Ex.Scene.DepthSortMode.FrontToBack); //Remember, the objects placed in the sorter *must* perform a valid CullTest, //if the CullTest simply returns true/false, no sorting will occur. //(Note the Actor.CullTest method) //to ease the CPU load, have the sorter only sort the actors every few frames... sorter.SortDelayFrameCount = 5; foreach (Actor actor in actors) sorter.Add(actor); // add the actors to the sorter (not the screen) //the sorter itself must be added to the screen! drawToScreen.Add(sorter); // the sorter will then draw the actors in a sorted order #else //In this case (on the Xbox), because the application is heavily vertex limited //and already heavily CPU stretched by the animation system, the cost of //sorting the actors actually causes a larger performance hit on the CPU than //the time saved on the GPU. This inballance causes a frame rate drop. // //However, the reason for this may be unexpected. //The framerate drop is not caused by the overhead of sorting the actors. // //Any 3D API calls made are doubly expensive on the XBOX, so in order to //maintain 20fps in this sample, the primary (rendering) thread must not //block, or switch to task processing. //If it does so, valuable rendering time is lost. // //When using a sorter, the actors are drawn in an order that is constantly changing. //However, they always have Update() called in a consistent order. // //During Update() the actors animation controllers will spawn thread tasks to //process their animation. // //These tasks are processed on the spare xbox hardware threads, they are //processed in the order they were added. //Processing the animation usually completes before the rendering finishes. //(the rendering is not delayed waiting for the animation to finish). // //However, when sorting the actors get drawn in an unpredictable order, //this means the last actor added could be the first actor to draw, //in such a case, the chances of it's animation processing having completed //is *very* low. When this happens, the rendering thread has to switch to //processing animations, delaying rendering. // //So, for the xbox, in this sample it's best just to draw in the update order. foreach (Actor actor in actors) drawToScreen.Add(actor); #endif //add the light source geometry, as they are usually below the actors, but above the ground foreach (IDraw geometry in lightSourceGeometry) drawToScreen.Add(geometry); //then add the ground plane, which is usually below the actors and lights. drawToScreen.Add(ground); //finally, enable a special feature of ElementRect. //This makes the element draw at the maximum possible Z distance //(behind anything else that has been drawn) background.DrawAtMaxZDepth = true; //add it to the screen drawToScreen.Add(background); } //finally, //create the draw statistics display stats = new Xen.Ex.Graphics2D.Statistics.DrawStatisticsDisplay(this.UpdateManager); drawToScreen.Add(stats); }
/// <summary> /// Draw all the model batch instances /// </summary> /// <param name="state"></param> public void Draw(DrawState state) { if (modelData == null) { throw new InvalidOperationException("ModelData is null"); } if (geometry == null) { SetupGeometry(); } int geometryIndex = 0; BatchModelShaderProvider shaderProvider = this.shaderProvider; MaterialLightCollection lights = this.lights; ShaderProviderFlag providerFlag; MaterialLightCollection.LightCollectionFlag lightsFlag; state.GetDrawFlag(out providerFlag); if (providerFlag.OverrideShaderProvider) { shaderProvider = providerFlag.ShaderProvider; } state.GetDrawFlag(out lightsFlag); if (lightsFlag.OverrideLightCollection) { lights = lightsFlag.LightCollection; } if (shaderProvider != null) { shaderProvider.BeginDraw(state); } //loop through the model data for (int m = 0; m < modelData.meshes.Length; m++) { MeshData mesh = modelData.meshes[m]; if (shaderProvider != null) { shaderProvider.BeginMesh(state, mesh); } for (int g = 0; g < mesh.geometry.Length; g++) { GeometryData geom = mesh.geometry[g]; GeometrySet set = this.geometry[geometryIndex]; if (set.count > 0) { bool instancing = state.SupportsHardwareInstancing && set.count > 2; if (shaderProvider == null || !shaderProvider.BeginGeometryShaderOverride(state, geom, lights, instancing)) { MaterialShader shader = geom.MaterialShader; shader.AnimationTransforms = null; shader.UseHardwareInstancing = instancing; shader.Lights = lights; shader.Bind(state); } //draw the geometry if (instancing) { state.DrawBatch(geom.Vertices, geom.Indices, PrimitiveType.TriangleList, null, set.instances, set.count); } else { for (int i = 0; i < set.count; i++) { state.PushWorldMatrixMultiply(ref set.instances[i]); geom.Vertices.Draw(state, geom.Indices, PrimitiveType.TriangleList); state.PopWorldMatrix(); } } if (shaderProvider != null) { shaderProvider.EndGeometry(state, geom); } } set.count = 0; geometryIndex++; } if (shaderProvider != null) { shaderProvider.EndMesh(state, mesh); } } if (shaderProvider != null) { shaderProvider.EndDraw(state); } drawCount = 0; }
protected override void Initialise() { //setup ambient lighting this.ambientLight = new MaterialLightCollection(); ambientLight.LightingEnabled = true; ambientLight.AmbientLightColour = new Vector3(0.25f, 0.25f, 0.25f); ambientLight.AddDirectionalLight(false, new Vector3(-1, -1, 0), new Vector3(2, 2, 2)); // add some backlighting //setup the shadow render camera Camera3D shadowCamera = new Camera3D(); shadowCamera.LookAt(new Vector3(1, 1, 3), new Vector3(-15, 20, 20), new Vector3(0, 0, 1)); //set the clip plane distances shadowCamera.Projection.FarClip = 40; shadowCamera.Projection.NearClip = 20; shadowCamera.Projection.FieldOfView *= 0.25f; //8bit is actually enough accuracy for this sample (given the limited range of the shadow) SurfaceFormat textureFormat = SurfaceFormat.Color; const int resolution = 512; //create the shadow map texture: drawShadowDepth = new DrawTargetTexture2D(shadowCamera, resolution, resolution, textureFormat, DepthFormat.Depth24); drawShadowDepth.ClearBuffer.ClearColour = Color.White; //for the shadow technique used, the shadow buffer is blurred. //this requires an intermediate render target on the PC DrawTargetTexture2D blurIntermediate = null; #if !XBOX360 //not required on the xbox if the render target is small enough to fit in EDRAM in one tile blurIntermediate = new DrawTargetTexture2D(shadowCamera, resolution, resolution, textureFormat); #endif //create a blur filter shadowDepthBlurFilter = new Xen.Ex.Filters.BlurFilter(Xen.Ex.Filters.BlurFilterFormat.SevenSampleBlur, 1.0f, drawShadowDepth, blurIntermediate); //create the scene camera Camera3D camera = new Camera3D(); camera.LookAt(new Vector3(0, 0, 3), new Vector3(10, 10, 6), new Vector3(0, 0, 1)); camera.Projection.FieldOfView *= 0.55f; //create the draw target. drawToScreen = new DrawTargetScreen(this, camera); drawToScreen.ClearBuffer.ClearColour = Color.Black; //the 'scene' //A DrawList from Tutorial 23 is used here, this stores the 'scene', //which is just a set of actors and the ground Tutorials.Tutorial_23.DrawList scene = new Tutorials.Tutorial_23.DrawList(); for (int x = 0; x < 2; x++) { for (int y = 0; y < 2; y++) { //create the actor instances if (x != 0 || y != 0) { scene.Add(new Actor(this.Content, new Vector3(x * 6 - 3, y * 6 - 3, 0), (x + y * 2 + 1) * 0.1f, 4 - x * 2 - y)); } } } //add the ground GroundDisk ground = new GroundDisk(this.Content, 10, ambientLight); scene.Add(ground); //setup the draw targets... //create the shader provider ShadowOutputShaderProvider shadowOutputShaderProvider = new ShadowOutputShaderProvider(); //add a ShadowMapDrawer to the shadow map texture drawShadowDepth.Add(new ShadowMapDrawer(scene, shadowOutputShaderProvider)); //setup the scene to be drawn to the screen //draw the scene normally (no shadow, just ambient) drawToScreen.Add(scene); //then draw the scene with a shadow (blended on top) drawToScreen.Add(new ShadowedSceneDrawer(scene, shadowOutputShaderProvider, drawShadowDepth)); //add a nice faded background Tutorial_20.BackgroundGradient background = new Tutorial_20.BackgroundGradient(Color.WhiteSmoke, Color.Black); background.DrawAtMaxZDepth = true; drawToScreen.Add(background); //create a textured element that will display the shadow map texture TexturedElement shadowDepthDisplay = new TexturedElement(drawShadowDepth, new Vector2(256, 256)); shadowDepthDisplay.VerticalAlignment = VerticalAlignment.Top; this.drawToScreen.Add(shadowDepthDisplay); }
protected override void Initialise() { camera = new Camera3D(); //create the draw target. drawToScreen = new DrawTargetScreen(camera); //clear to dark blue drawToScreen.ClearBuffer.ClearColour = new Color(20, 20, 40); //create the light collection lights = new MaterialLightCollection(); //set a dark blue ambient colour lights.AmbientLightColour = new Color(40, 40, 80).ToVector3(); //get a list of predifined colours in the 'Color' structure using reflection //avoid doing this sort of thing at runtime! PropertyInfo[] colours = typeof(Color).GetProperties(BindingFlags.Static | BindingFlags.Public); // get all the static properties Random random = new Random(); int lightCount = 12; //geometry for a light (shared for each light) IDraw lightGeometry = null; for (int i = 0; i < lightCount; i++) { //start with white. Color colour = Color.White; //try and pick a random colour from the list, using reflection to get the value of the property try { //pick a random field info object (a reflected colour property) var randomColourField = colours[random.Next(colours.Length)]; //try and get it's value object colourObject = randomColourField.GetValue(null, null); if (colourObject is Color) colour = (Color)colourObject; } catch { //this shouldn't fail, but always be careful with reflection... //typically this would be handled correctly, but here, just stick with white. } float angle = (float)i / (float)lightCount * (float)Math.PI * 2; Vector3 position = new Vector3((float)Math.Sin(angle) * (diskRadius + 1), (float)Math.Cos(angle) * (diskRadius + 1), 4); float intensity = 1; //interface to the light about to be created IMaterialPointLight light = null; //create the point light light = lights.CreatePointLight(position, intensity, colour, colour); light.SourceRadius = 5; //create the light geometry (a sphere) if (lightGeometry == null) lightGeometry = new Xen.Ex.Geometry.Sphere(Vector3.One, 8, true, false, false); //visually show the light with a light drawer IDraw lightSourceDrawer = new LightSourceDrawer(position, lightGeometry, colour); //add the light geometry to the screen drawToScreen.Add(lightSourceDrawer); } //add the actor actor = new Tutorials.Tutorial_11.Actor(this.Content, this.lights); drawToScreen.Add(actor); //create the ground disk GroundDisk ground = new GroundDisk(this.Content, lights, diskRadius); //then add it to the screen drawToScreen.Add(ground); }
protected override void Initialise() { Resource.EnableResourceTracking(); camera = new Camera3D(); camera.Projection.FarClip = 300; camera.Projection.NearClip = 10; camera.Projection.FieldOfView *= 0.55f; //create the draw target. drawToScreen = new DrawTargetScreen(this, camera); //no need to clear the colour buffer, as a special background will be drawn drawToScreen.ClearBuffer.ClearColourEnabled = false; //create the light collection first lights = new MaterialLightCollection(); // In this example, the rendering order has been manually optimized to reduce the number of pixels drawn // // In xen, rendering is usually explicit. This means, when a call to Draw() is made, the draw order is // respected, and internally the object will be drawn using the graphics API. // Objects added to the screen will have Draw() called in the order they were added. // // However, the draw order can also cause performance problems. // In general, it's best to draw front to back, this means draw the objects closest to the screen first. // // This way, the objects at the back will be drawing behind the objects already drawn. // Modern video cards can quickly discard pixels if they are 'behind' what is already drawn. // Without front-to-back, the objects at the front could be drawing *over* objects already drawn. // // This is known as overdraw, a case where an object is drawn, only to be 'overdrawn' later in the frame. // Reducing overdraw can help performance, especially when complex shaders are used. // // In this example, the sample is usually vertex-limited (that is, the bottleneck is vertex processing) // However, it can demonstrate how optimizing for overdraw can significantly reduce the number of pixels // that are shaded. // // In debug builds, the DrawStatisticsDisplay class will show the number of pixels drawn in the frame. // // With overdraw optimized draw order, ~1,000,000 pixels are drawn per frame. Without, upto 2,100,000 // pixels are drawn per frame (usually ~1,800,000). (A 1280x720 display has 921,600 pixels) // // This means that without an overdraw optimized draw order, on average, each pixel is being drawn // twice. With an optimized draw order, this number is closer to 1.1, which is very close to the // optimal value of 1.0 (where each pixel is only drawn once). // // Note that the number of pixels reported by the DrawStatisticsDisplay is for the entire frame, including // every render target. Some PCs may not support this value, and display -1. // // One last point.... // This sample is an extreme test of a GPU's ability to push triangles onto the screen (vertex/triangle rate). // However, observation will show the number of triangles drawn is often over 3,300,000! // Assuming half the triangles are back-face culled (an accurate approximation), this still means // there are around 1,650,000 triangles that are visible at any time. // (But remember, the vertex shader still runs for back facing triangles!) // // Assuming approximatly half of these triangles are depth occluded (very approximate), still // results in a huge number of visible triangles. // This all means that the average triangle is drawing a *very* small number of pixels, in this case, // the average for the actors is probably *less than 1 pixel per triangle!*. // // Triangles averaging less than 1 pixel are known as subpixel triangles. // For a number of reasons, subpixel triangles are very inefficent. // For example, if a single pixel is drawn, due to the way a video card works, the pixel shader will always // run in multiples of 4 pixels, so a single pixel triangle will still run the pixel shader 4 times. // // This makes this sample a perfect candidate for level of detail optimization, where a lower resolution // model is used as an actor gets further away from the screen. // // The vertex shader is also very expensive, and for each triangle, it will be run upto 3 times. // This means the vertex shader is quite possibly running more often than the pixel shader. // This hypothesis can be confirmed; setting the lights to per-vertex, instead of per-pixel, results // in a significantly *lower* frame rate! // // // bool optimizeForOverdraw = true; //create a list of actors to added to the screen List <Actor> actors = new List <Actor>(500); //create 500 actors! for (int i = 0; i < 500; i++) { Actor actor = new Actor(this.Content, this.UpdateManager, lights, diskRadius); actors.Add(actor); } //create the lights, similar to Tutorial 14 lights.AmbientLightColour = new Vector3(0.45f, 0.45f, 0.5f); Vector3[] lightPositions = new Vector3[] { new Vector3(0, 30, 12), new Vector3(0, -30, 12) }; //setup the two lights in the scene IDraw lightGeometry = null; IDraw lightPoleGeometry = null; //create geometry to display the lights List <IDraw> lightSourceGeometry = new List <IDraw>(); //setup the lights, and create the light globe geometry for (int i = 0; i < lightPositions.Length; i++) { Vector3 colour = new Vector3(5, 5, 5); IMaterialPointLight light = lights.AddPointLight(i < 2, lightPositions[i], 8, colour, colour); light.ConstantAttenuation = 0.25f; // make the ligh falloff curve a lot sharper (brighter at the centre) light.LinearAttenuation = 0; light.QuadraticAttenuation = 0.075f; //approximate inverse distance in which the brightness of the light will halve if (lightGeometry == null) { lightGeometry = new Xen.Ex.Geometry.Sphere(Vector3.One, 8, true, false, false); lightPoleGeometry = new Xen.Ex.Geometry.Cube(new Vector3(0.4f, 0.4f, lightPositions[i].Z * 0.5f)); } //visually show the light //create the light sphere geometry from tutorial 14. Vector3 position = lightPositions[i]; lightSourceGeometry.Add(new Tutorial_14.LightSourceDrawer(position, lightGeometry, Color.LightYellow)); position.Z *= 0.5f; lightSourceGeometry.Add(new Tutorial_14.LightSourceDrawer(position, lightPoleGeometry, new Color(40, 40, 70))); } //create the ground plane, also from tutorial 14 Tutorial_14.GroundDisk ground = new Tutorial_14.GroundDisk(this.Content, lights, diskRadius); //this is a special background element, //it draws a gradient over the entire screen, fading from dark at the bottom to light at the top. Color darkBlue = new Color(40, 40, 50); Color lightBlue = new Color(100, 100, 110); BackgroundGradient background = new BackgroundGradient(lightBlue, darkBlue); if (optimizeForOverdraw == false) { //add all the objects in a naive order //first add the background (fills the entire screen, draws to every pixel, but is very fast) drawToScreen.Add(background); //then add the ground plane (all the actors will appear on top of the ground plane, overdrawing it) drawToScreen.Add(ground); //then add the lights (which are on top of the ground, overdrawing it) foreach (IDraw geometry in lightSourceGeometry) { drawToScreen.Add(geometry); } //then finally add the actors, in the order they were created foreach (Actor actor in actors) { drawToScreen.Add(actor); } } else { //or, add the objects in a order optimized for overdraw #if !XBOX360 //first, add the actors. Because they are almost always closest to the screen //however, use a depth sorter so the actors are sorted into a front to back draw order, //this sorting is based on the centre point of the cull tests they perform. Xen.Ex.Scene.DepthDrawSorter sorter = new Xen.Ex.Scene.DepthDrawSorter(Xen.Ex.Scene.DepthSortMode.FrontToBack); //Remember, the objects placed in the sorter *must* perform a valid CullTest, //if the CullTest simply returns true/false, no sorting will occur. //(Note the Actor.CullTest method) //to ease the CPU load, have the sorter only sort the actors every few frames... sorter.SortDelayFrameCount = 5; foreach (Actor actor in actors) { sorter.Add(actor); // add the actors to the sorter (not the screen) } //the sorter itself must be added to the screen! drawToScreen.Add(sorter); // the sorter will then draw the actors in a sorted order #else //In this case (on the Xbox), because the application is heavily vertex limited //and already heavily CPU stretched by the animation system, the cost of //sorting the actors actually causes a larger performance hit on the CPU than //the time saved on the GPU. This inballance causes a frame rate drop. // //However, the reason for this may be unexpected. //The framerate drop is not caused by the overhead of sorting the actors. // //Any 3D API calls made are doubly expensive on the XBOX, so in order to //maintain 20fps in this sample, the primary (rendering) thread must not //block, or switch to task processing. //If it does so, valuable rendering time is lost. // //When using a sorter, the actors are drawn in an order that is constantly changing. //However, they always have Update() called in a consistent order. // //During Update() the actors animation controllers will spawn thread tasks to //process their animation. // //These tasks are processed on the three spare xbox hardware threads, they are //processed in the order they were added. //Processing the animation usually completes before the rendering finishes. //(the rendering is not delayed waiting for the animation to finish). // //However, when sorting the actors get drawn in an unpredictable order, //this means the last actor added could be the first actor to draw, //in such a case, the chances of it's animation processing having completed //is *very* low. When this happens, the rendering thread has to switch to //processing animations, delaying rendering. // //So, for the xbox, it's best just to draw in the update order. foreach (Actor actor in actors) { drawToScreen.Add(actor); } #endif //add the light source geometry, as they are usually below the actors, but above the ground foreach (IDraw geometry in lightSourceGeometry) { drawToScreen.Add(geometry); } //then add the ground plane, which is usually below the actors and lights. drawToScreen.Add(ground); //finally, enable a special feature of ElementRect. //This makes the element draw at the maximum possible Z distance //(behind anything else that has been drawn) background.DrawAtMaxZDepth = true; //add it to the screen drawToScreen.Add(background); } //finally, //create the draw statistics display stats = new Xen.Ex.Graphics2D.Statistics.DrawStatisticsDisplay(this.UpdateManager); drawToScreen.Add(stats); }
public GeometryDrawer(Vector3 position) { //create the quad geometry = new DynamicQuadGeometry(); //setup the world matrix worldMatrix = Matrix.CreateTranslation(position); //create a basic lighting shader with some average looking lighting :-) MaterialShader material = new MaterialShader(); material.SpecularColour = Color.LightYellow.ToVector3() * 0.5f; Vector3 lightDirection = new Vector3(-1, -1, -1); //a dramatic direction //Note: To use vertex colours with a MaterialShader, UseVertexColour has to be set to true material.UseVertexColour = true; //create a directional light MaterialLightCollection lights = new MaterialLightCollection(); lights.CreateDirectionalLight(-lightDirection, Color.WhiteSmoke); material.LightCollection = lights; this.shader = material; }
protected override void Initialise() { camera = new Camera3D(); //create the draw target. drawToScreen = new DrawTargetScreen(camera); //clear to dark blue drawToScreen.ClearBuffer.ClearColour = new Color(20, 20, 40); //create the light collection lights = new MaterialLightCollection(); //set a dark blue ambient colour lights.AmbientLightColour = new Color(40, 40, 80).ToVector3(); //positions for two lights Vector3[] lightPositions = new Vector3[] { new Vector3(0, 30, 4), new Vector3(0, -30, 4) }; //geometry for a light (shared for each light) IDraw lightGeometry = null; for (int i = 0; i < lightPositions.Length; i++) { float intensity = 2; Color lightColor = Color.LightYellow; Color lightSpecularColour = Color.WhiteSmoke; //interface to the light about to be created IMaterialPointLight light = null; //create the point light light = lights.CreatePointLight(lightPositions[i], intensity, lightColor, lightSpecularColour); //Adjusting this value controls how quickly the light falloff occurs. //A larger value will produce a slower falloff, and result in a softer, brighter light. //A smaller value will produce a darker, but sharper light source. //Generally, if you reduce this value, increase the intensity to compensate. light.SourceRadius = 4; //create the light geometry (a sphere) if (lightGeometry == null) lightGeometry = new Xen.Ex.Geometry.Sphere(Vector3.One, 8, true, false, false); //visually show the light with a light drawer var lightSourceDrawer = new LightSourceDrawer(lightPositions[i], lightGeometry,lightColor); //add the light geometry to the screen drawToScreen.Add(lightSourceDrawer); } //create the ground disk var ground = new GroundDisk(this.Content, lights, diskRadius); //then add it to the screen drawToScreen.Add(ground); }
public GeometryDrawer(Vector3 position) { //NEW CODE //create the quad this.geometry = new QuadGeometry(); //setup the world matrix this.worldMatrix = Matrix.CreateTranslation(position); //create a lighting shader with some average looking lighting :-) var material = new MaterialShader(); material.SpecularColour = Color.LightYellow.ToVector3();//with a nice sheen var lightDirection = new Vector3(0.5f,1,-0.5f); //a less dramatic direction var lights = new MaterialLightCollection(); lights.AmbientLightColour = Color.DarkGoldenrod.ToVector3() * 0.5f; lights.CreateDirectionalLight(-lightDirection, Color.WhiteSmoke); material.LightCollection = lights; this.shader = material; }
protected override void Initialise() { Camera3D camera = new Camera3D(); camera.LookAt(Vector3.Zero, new Vector3(0, 0, 5), Vector3.UnitY); //create the draw target. drawToScreen = new DrawTargetScreen(camera); drawToScreen.ClearBuffer.ClearColour = Color.CornflowerBlue; //create a shader to display the geometry (this is the same as tutorial 02) var lightDirection = new Vector3(1.0f, 0.5f, 0.5f); var material = new MaterialShader(); material.SpecularColour = Color.LightYellow.ToVector3(); //give the material a nice sheen var lights = new MaterialLightCollection(); lights.AmbientLightColour = Color.CornflowerBlue.ToVector3() * 0.5f; //set the ambient lights.CreateDirectionalLight(lightDirection, Color.Gray); //add the first of two light sources lights.CreateDirectionalLight(-lightDirection, Color.DarkSlateBlue); material.LightCollection = lights; //create a simpler shader to display the wireframe (and also used for the bounding cube) var simpleShader = new Xen.Ex.Shaders.FillSolidColour(); simpleShader.FillColour = Vector4.One * 0.01f; var sphereSize = new Vector3(0.5f, 0.5f, 0.5f); //create the complex sphere, this will have ~100k triangles. //pass in a shader for wireframe rendering sphere = new GeometryDrawer(new Xen.Ex.Geometry.Sphere(sphereSize, 200), material, simpleShader); //create the bounding cube sphereBoundingBox = new GeometryDrawer(new Xen.Ex.Geometry.Cube(sphereSize), simpleShader, null); //create the occluding cube, and position it close to the camera cube = new GeometryDrawer(new Xen.Ex.Geometry.Cube(Vector3.One), material, null); cube.position = new Vector3(0, 0, 2.75f); //add the cube first (so it can draw first, potentially occluding the sphere) //if the cube was added second, it would have no effect, as it would draw after the sphere drawToScreen.Add(cube); //create the predicate, passing in the sphere and bounding box var predicate = new Xen.Ex.Scene.DrawPredicate(sphere, sphereBoundingBox); //add the DrawPredicate (the DrawPredicate draws it's children) drawToScreen.Add(predicate); //statistic overlay statOverlay = new Xen.Ex.Graphics2D.Statistics.DrawStatisticsDisplay(this.UpdateManager); drawToScreen.Add(statOverlay); }
//constructor public SphereDrawer(Vector3 position) { //setup the sphere var size = new Vector3(1,1,1); //use a prebuilt sphere geometry class sphereGeometry = new Sphere(size, 32); //setup the world matrix worldMatrix = Matrix.CreateTranslation(position); //create a lighting shader with some nice looking lighting var material = new MaterialShader(); material.SpecularColour = Color.LightYellow.ToVector3();//with a nice sheen var lightDirection = new Vector3(0.5f,1,-0.5f); //a dramatic direction var lights = new MaterialLightCollection(); lights.AmbientLightColour = Color.CornflowerBlue.ToVector3() * 0.5f; lights.CreateDirectionalLight(lightDirection, Color.Gray);//two light sources lights.CreateDirectionalLight(-lightDirection, Color.DarkSlateBlue); material.LightCollection = lights; this.shader = material; }