Вернемся к нашим урокам. Третий урок посвящен создания шейдера. В прошлой статье уже описывался метод генерации C# классов для шейдерных эффектов, который применяется в Xen, так что сейчас мы это опустим и перейдем к самому уроку.
В папке с уроком находятся несколько файлов, откроем сначала shader.fx (комментарии убраны)
float4x4 worldViewProj : WORLDVIEWPROJECTION;
float4 colour : GLOBAL;
//finally, a constant without a semantic:
float scale = 1;
//--------------------------------------------------------------//
// vertex shader, scales the mesh by the scale attribute
//--------------------------------------------------------------//
void Tutorial03VS(
float4 position : POSITION,
out float4 positionOut : POSITION)
{
position.xyz *= scale;
positionOut = mul(position,worldViewProj);
}
//--------------------------------------------------------------//
// pixel shader, returns the global colour
//--------------------------------------------------------------//
float4 Tutorial03PS() : COLOR
{
return colour;
}
//--------------------------------------------------------------//
// Technique that uses the shaders (a class will be generated)
//--------------------------------------------------------------//
technique Tutorial03Technique
{
pass
{
VertexShader = compile vs_2_0 Tutorial03VS();
PixelShader = compile ps_2_0 Tutorial03PS();
}
}
Мы видим, что этот шейдер просто масштабирует модель, перед тем как пересчитать ее координаты. Пиксельный шейдер просто возвращает константу, в качестве цвета модели.
Взглянем теперь на сгенерированный класс, который хранится в файле shader.fx.cs (я удалил часть кода, которая содержала скомпилированный байт-код эффекта, чтобы статья была более читаемой).
// XenFX // Assembly = Xen.Graphics.ShaderSystem.CustomTool, Version=7.0.1.1, Culture=neutral, PublicKeyToken=e706afd07878dfca // SourceFile = shader.fx // Namespace = Tutorials.Tutorial_03 namespace Tutorials.Tutorial_03.Shader { /// <summary><para>Technique 'Tutorial03Technique' generated from file 'shader.fx'</para><para>Vertex Shader: approximately 6 instruction slots used, 5 registers</para><para>Pixel Shader: approximately 1 instruction slot used, 1 register</para></summary> [System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("Xen.Graphics.ShaderSystem.CustomTool.dll", "5040fe79-bab4-4d2f-9335-463d3794d366")] public sealed class Tutorial03Technique : Xen.Graphics.ShaderSystem.BaseShader { /// <summary>Construct an instance of the 'Tutorial03Technique' shader</summary> public Tutorial03Technique() { this.vreg[4] = new Microsoft.Xna.Framework.Vector4(1F, 0F, 0F, 0F); this.sc0 = -1; this.sc1 = -1; this.sc2 = -1; this.gc0 = -1; } /// <summary>Setup shader static values</summary><param name="state"/> private void gdInit(Xen.Graphics.ShaderSystem.ShaderSystemBase state) { // set the graphics ID Tutorial03Technique.gd = state.DeviceUniqueIndex; this.GraphicsID = state.DeviceUniqueIndex; Tutorial03Technique.cid0 = state.GetNameUniqueID("scale"); Tutorial03Technique.gid0 = state.GetGlobalUniqueID<Microsoft.Xna.Framework.Vector4>("colour"); } /// <summary>Bind the shader, 'ic' indicates the shader instance has changed and 'ec' indicates the extension has changed.</summary><param name="state"/><param name="ic"/><param name="ec"/><param name="ext"/> protected override void BeginImpl(Xen.Graphics.ShaderSystem.ShaderSystemBase state, bool ic, bool ec, Xen.Graphics.ShaderSystem.ShaderExtension ext) { // if the device changed, call Warm() if ((state.DeviceUniqueIndex != Tutorial03Technique.gd)) { this.WarmShader(state); ic = true; } // Force updating if the instance has changed this.vreg_change = (this.vreg_change | ic); this.preg_change = (this.preg_change | ic); this.vbreg_change = (this.vbreg_change | ic); this.vireg_change = (this.vireg_change | ic); // Set the value for attribute 'worldViewProj' this.vreg_change = (this.vreg_change | state.SetWorldViewProjectionMatrix(ref this.vreg[0], ref this.vreg[1], ref this.vreg[2], ref this.vreg[3], ref this.sc0)); // Set the value for global 'colour' this.preg_change = (this.preg_change | state.SetGlobalVector4(ref this.preg[0], Tutorial03Technique.gid0, ref this.gc0)); if ((this.vreg_change == true)) { Tutorial03Technique.fx.vs_c.SetValue(this.vreg); this.vreg_change = false; ic = true; } if ((this.preg_change == true)) { Tutorial03Technique.fx.ps_c.SetValue(this.preg); this.preg_change = false; ic = true; } if ((ext == Xen.Graphics.ShaderSystem.ShaderExtension.Blending)) { ic = (ic | state.SetBlendMatricesDirect(Tutorial03Technique.fx.vsb_c, ref this.sc1)); } if ((ext == Xen.Graphics.ShaderSystem.ShaderExtension.Instancing)) { this.vireg_change = (this.vireg_change | state.SetViewProjectionMatrix(ref this.vireg[0], ref this.vireg[1], ref this.vireg[2], ref this.vireg[3], ref this.sc2)); if ((this.vireg_change == true)) { Tutorial03Technique.fx.vsi_c.SetValue(this.vireg); this.vireg_change = false; ic = true; } } // Finally, bind the effect if ((ic | ec)) { state.SetEffect(this, ref Tutorial03Technique.fx, ext); } } /// <summary>Warm (Preload) the shader</summary><param name="state"/> protected override void WarmShader(Xen.Graphics.ShaderSystem.ShaderSystemBase state) { // Shader is already warmed if ((Tutorial03Technique.gd == state.DeviceUniqueIndex)) { return; } // Setup the shader if ((Tutorial03Technique.gd != state.DeviceUniqueIndex)) { this.gdInit(state); } Tutorial03Technique.fx.Dispose(); // Create the effect instance state.CreateEffect(out Tutorial03Technique.fx, Tutorial03Technique.fxb, 7, 1); } /// <summary>True if a shader constant has changed since the last Bind()</summary> protected override bool Changed() { return (this.vreg_change | this.preg_change); } /// <summary>Returns the number of vertex inputs used by this shader</summary> protected override int GetVertexInputCountImpl() { return 1; } /// <summary>Returns a vertex input used by this shader</summary><param name="i"/><param name="usage"/><param name="index"/> protected override void GetVertexInputImpl(int i, out Microsoft.Xna.Framework.Graphics.VertexElementUsage usage, out int index) { usage = ((Microsoft.Xna.Framework.Graphics.VertexElementUsage)(Tutorial03Technique.vin[i])); index = Tutorial03Technique.vin[(i + 1)]; } /// <summary>Static graphics ID</summary> private static int gd; /// <summary>Static effect container instance</summary> private static Xen.Graphics.ShaderSystem.ShaderEffect fx; /// <summary/> private bool vreg_change; /// <summary/> private bool preg_change; /// <summary/> private bool vbreg_change; /// <summary/> private bool vireg_change; /// <summary>Return the supported modes for this shader</summary><param name="blendingSupport"/><param name="instancingSupport"/> protected override void GetExtensionSupportImpl(out bool blendingSupport, out bool instancingSupport) { blendingSupport = true; instancingSupport = true; } /// <summary>Name ID for 'scale'</summary> private static int cid0; /// <summary>Assign the shader value 'float scale'</summary> public float Scale { set { this.vreg[4] = new Microsoft.Xna.Framework.Vector4(value, 0F, 0F, 0F); this.vreg_change = true; } } /// <summary>Change ID for Semantic bound attribute 'worldViewProj'</summary> private int sc0; /// <summary>Change ID for Semantic bound attribute '__BLENDMATRICES__GENMATRIX'</summary> private int sc1; /// <summary>Change ID for Semantic bound attribute '__VIEWPROJECTION__GENMATRIX'</summary> private int sc2; /// <summary>TypeID for global attribute 'float4 colour'</summary> private static int gid0; /// <summary>Change ID for global attribute 'float4 colour'</summary> private int gc0; /// <summary>array storing vertex usages, and element indices</summary> readonly private static int[] vin = new int[] {0,0}; /// <summary>Vertex shader register storage</summary> readonly private Microsoft.Xna.Framework.Vector4[] vreg = new Microsoft.Xna.Framework.Vector4[5]; /// <summary>Pixel shader register storage</summary> readonly private Microsoft.Xna.Framework.Vector4[] preg = new Microsoft.Xna.Framework.Vector4[1]; /// <summary>Instancing shader register storage</summary> readonly private Microsoft.Xna.Framework.Vector4[] vireg = new Microsoft.Xna.Framework.Vector4[4]; /// <summary>Set a shader attribute of type 'Single' by global unique ID, see <see cref="Xen.Graphics.ShaderSystem.ShaderSystemBase.GetNameUniqueID"/> for details.</summary><param name="state"/><param name="id"/><param name="value"/> protected override bool SetAttributeImpl(Xen.Graphics.ShaderSystem.ShaderSystemBase state, int id, float value) { if ((Tutorial03Technique.gd != state.DeviceUniqueIndex)) { this.WarmShader(state); } if ((id == Tutorial03Technique.cid0)) { this.Scale = value; return true; } return false; } } }
Отметим, что название класса совпадает с названием единственной техники в шейдере. А название пространства имен совпадает с названием самого файла с шейдером.
Также обратим внимание на то, что наш новый класс имеет лишь одно публичное свойство. Дело в том, что остальные параметры заданы со специальной семантикой.
Например, семантика WORLDVIEWPROJECTION говорит Xen о том, что нужно автоматически передавать произведение мировой матрицы, матрицы вида и матрицы проекции в качестве этого параметра.
Семантика GLOBAL сообщает о том, что значение переменной будет задаваться специальными способом, при котором значение будет принадлежать не только данному шейдеру, а будет доступно для всех шейдоров.
Посмотрим, что происходит в коде самого урока. Рассмотрим новый код из класса SphereDrawer:
private Shader.Tutorial03Technique shader;
public void Draw(DrawState state) { using (state.WorldMatrix.PushMultiply(ref worldMatrix)) { if (sphereGeometry.CullTest(state)) {
float scaleValue = (float)Math.Sin(state.TotalTimeSeconds) * 0.5f + 1.0f; shader.Scale = scaleValue; using (state.Shader.Push(shader)) {
sphereGeometry.Draw(state); } } } }
Тут мы просто задаем параметр Scale для эффекта.
В коде основного класса Tutorial изменился только метод Frame:
//main application draw method protected override void Frame(FrameState state) { //NEW CODE //set the global float4 attribute named 'colour' to 1,0,0,1, which is bright red with an alpha of 1. state.ShaderGlobals.SetShaderGlobal("colour", new Vector4(1, 0, 0, 1)); //draw to the screen. drawToScreen.Draw(state); }
В этом месте устанавливается значение глобальной переменной colour.
Исходный код урока:
using System; using System.Collections.Generic; using System.Text; using Xen; using Xen.Camera; using Xen.Graphics; using Xen.Ex.Geometry; using Xen.Ex.Material; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; /* * This sample modifies Tutorial_02 (Draw Sphere) * This sample demonstrates: * * creating and using a custom shader * * --------------------------------------------------------------- * Before reading further, please read the comments in 'shader.fx' * --------------------------------------------------------------- * * * see the 'NEW CODE' comments for code that has changed in this tutorial * */ namespace Tutorials.Tutorial_03 { //this class is mostly the same as the Draw Sphere tutorial, //except the shader is hard coded to the custom shader class SphereDrawer : IDraw { private Xen.Ex.Geometry.Sphere sphereGeometry; private Matrix worldMatrix; //NEW CODE //the shader class has been generated in the namespace 'Shader', because the filename is 'shader.fx'. //The only technique in the file is named 'Tutorial03Technique'. //The class that was generated is Shader.Tutorial03Technique: private Shader.Tutorial03Technique shader; public SphereDrawer(Vector3 position) { //setup the sphere Vector3 size = new Vector3(1, 1, 1); this.sphereGeometry = new Sphere(size, 32); //setup the world matrix this.worldMatrix = Matrix.CreateTranslation(position); //NEW CODE //create an instance of the shader: this.shader = new Shader.Tutorial03Technique(); //Note: All shaders implement the 'IShader' interface } public void Draw(DrawState state) { using (state.WorldMatrix.PushMultiply(ref worldMatrix)) { //cull test the sphere (note, the cull test uses the current world matrix) if (sphereGeometry.CullTest(state)) { //NEW CODE //In this sample, the shader instance is defined in this class, however //the draw state can be used to get a shared static instance of a shader. //Getting shader instances in this way can reduce memory usage. Eg: // // var shader = state.GetShader<Shader.Tutorial03Technique>(); // //compute a scale value that follows a sin wave float scaleValue = (float)Math.Sin(state.TotalTimeSeconds) * 0.5f + 1.0f; //Set the scale value (scale is declared in the shader source) shader.Scale = scaleValue; //Bind the custom shader instance using (state.Shader.Push(shader)) { //draw the sphere geometry sphereGeometry.Draw(state); } } //xen will reset the world matrix when the using statement finishes: } } //always draw.. don't cull yet public bool CullTest(ICuller culler) { return true; } } //an application that draws a sphere in the middle of the screen [DisplayName(Name = "Tutorial 03: Custom Shader")] public class Tutorial : Application { //a DrawTargetScreen is a draw target that draws items directly to the screen. //in this case it will only draw a SphereDrawer private DrawTargetScreen drawToScreen; protected override void Initialise() { Camera3D camera = new Camera3D(); camera.LookAt(Vector3.Zero, new Vector3(0, 0, 4), Vector3.UnitY); //create the draw target. drawToScreen = new DrawTargetScreen(camera); drawToScreen.ClearBuffer.ClearColour = Color.CornflowerBlue; //create the sphere SphereDrawer sphere = new SphereDrawer(Vector3.Zero); //add it to be drawn to the screen drawToScreen.Add(sphere); } //main application draw method protected override void Frame(FrameState state) { //NEW CODE //set the global float4 attribute named 'colour' to 1,0,0,1, which is bright red with an alpha of 1. state.ShaderGlobals.SetShaderGlobal("colour", new Vector4(1, 0, 0, 1)); //draw to the screen. drawToScreen.Draw(state); } protected override void Update(UpdateState state) { if (state.PlayerInput[PlayerIndex.One].InputState.Buttons.Back.OnPressed) this.Shutdown(); } } }
Комментариев нет:
Отправить комментарий