Not exactly toolset related, but I wrote up some stuff about how to modify the shaders. I'll make it a fairly continuous series on computer graphics if people are interested.
http://social.bioware.com/125615/blog/
Warning: it's really long. I tried to be as friendly to graphics newbies as possible - drop me a line if you have any questions.
Let's talk about shaders
Débuté par
Driveninhifi
, nov. 17 2009 07:21
#1
Posté 17 novembre 2009 - 07:21
#2
Posté 17 novembre 2009 - 07:40
Bumping this so people can see it. Let me know if you have any questions or something you'd like to see.
#3
Posté 18 novembre 2009 - 02:07
Cool.
Modifié par Periodiko, 18 novembre 2009 - 02:08 .
#4
Posté 18 novembre 2009 - 03:46
I hope you work in the graphics industry. That is good stuff.
#5
Posté 18 novembre 2009 - 06:48
Since I'd like some feedback, here's the info:
As a disclaimer, I don't work for Bioware - all this info is based on my poking at the game.
You will need the following:
We'll start simple - removing the wavyness in the blur of the Fade effect. But first, some background!
Dragon Age uses DirectX 9 and has a somewhat odd shader mechanism (more on that later). If you are unfamiliar with shaders you may want to read up a bit, but I will attempt to give a quick rundown:
DirectX contains 4 types of shaders, each operating in order. The pipeline itself actually has 7 stages (if you discount primitive assembly as a stage).
Typically, DirectX shaders are contained in FX files. These files can contain any number of different shaders organized by "Technique." So a single file may have a vertex shader, a pixel shader and a block that looks something like this:
We, however, are interested in changing what is known as a Post-Processing effect. This effect has no vertex piece - the final image has already been rendered. We will just be operating on that image and modifying it. Effects like this are blur, bloom, sepia and the radial blur/distortion that is applied to the screen when you are in the Fade.
Dragon Age does not use FX techniques for its postprocessing. This is likely because OpenGL does not support them without specific extensions - either the engine has some of NWN's guts (which was OpenGL) or because of the PS3 port. (The PS3 uses OpenGL ES, though the implementation is not ideal)
Instead, it loads shaders based off of XML files found in packages/core/data/postprocesseffects.erf. These do essentially the same thing - let's take a look at that file.You'll notice it's not very big. We're interested in da_fade.ffx.xml.
So, what can we gather from this file? Unfortunately a walkthrough is difficult, because it's an XML file, but bear with me as I hit the highlights:
So what have we discovered? We know that this effect takes 2 passes: one for the radial blur, and one for the distortion.
We also know the names of the shaders used, so we should go look for them.
For your reference, the shaders are located in packages/core/data/shaders.erf. Go ahead and open it in the Toolkit and check out the list.You'll notice that they have two extensions: psh.fxo and vsh.fxo, for pixel shaders and vertex shaders, respectively. We are interested in fadedistortino.psh.fxo.
You'll probably notice that opening this results in garbage. That's because it's a pre-compiled shader - which is what fxo denotes.
Next time I'll show you how to get it back into a readable form. For now, let's try replacing it with something else.
We know that the game will provide a copy of the framebuffer at texture 0. So let's try to simply pass through that information.
That pixel shader looks like this:
Simple, right? One last thing - we need this data in a format the game can consume.
The shaders are already compiled, so we probably can't just stick this shader in the override folder.
Fortunately, the DirectX SDK includes a shader compiler, FXC.
FXC is pretty simple, you give it an input file, a profile and output info.
First, let's see what our shader looks like compiled. /T tells fxc that this is a pixel shader of version 2.0.
Now we have our own fadedistortion.psh.fxo. Let's put that in the override folder and run the game.
If you start a new playthrough as a Mage you are almost immediately thrown into the Fade, so you can see if your effect works.

You may notice that this is a bit boring, so let's do something ridiculous:

Next time we'll get into more interesting effects, and talk about how to take a look at the game's existing shaders to see how they work.
As a disclaimer, I don't work for Bioware - all this info is based on my poking at the game.
You will need the following:
- Dragon Age
- Toolset
- The DirectX SDK from Microsoft
- A Text Editor
We'll start simple - removing the wavyness in the blur of the Fade effect. But first, some background!
Dragon Age uses DirectX 9 and has a somewhat odd shader mechanism (more on that later). If you are unfamiliar with shaders you may want to read up a bit, but I will attempt to give a quick rundown:
DirectX contains 4 types of shaders, each operating in order. The pipeline itself actually has 7 stages (if you discount primitive assembly as a stage).
- Vertex shaders, which operate on the geometric data passed in. They typically transform/rotate points then apply a projection matrix to make them appear 3d. They get points in and send points out, but cannot create new points.
- Tesselation shaders - the new kid on the block with DirectX 11. There are actually 2 of them: Hull Shaders and Domain Shaders. They are waaaaaay beyond the scope of this, but suffice to say they allow you to create complicated surfaces. Plus no one uses them yet, as there isn't much hardware support.
- Geometry shaders, which allow simple tesselation among other things - for example, a game could render particles by sending in a series of points that the shader converts into quads. This could save on the overhead of sending stuff to the graphics card - you would send 1 point per particle instead of 4.
- Pixel shaders: these are different than the other ones. This operates purely on the generated image - it does not touch the geometry at all. The shader outputs the final color of the image.
Typically, DirectX shaders are contained in FX files. These files can contain any number of different shaders organized by "Technique." So a single file may have a vertex shader, a pixel shader and a block that looks something like this:
technique MyRenderTechnique
{
pass P0
{
VertexShader= compile vs_2_0 vtx_main;
PixelShader=compile ps_2_0 ps_main;
}
}Which defines a rendering pass that executes vtx_main for the vertex stage, and ps_main for the pixel stage.We, however, are interested in changing what is known as a Post-Processing effect. This effect has no vertex piece - the final image has already been rendered. We will just be operating on that image and modifying it. Effects like this are blur, bloom, sepia and the radial blur/distortion that is applied to the screen when you are in the Fade.
Dragon Age does not use FX techniques for its postprocessing. This is likely because OpenGL does not support them without specific extensions - either the engine has some of NWN's guts (which was OpenGL) or because of the PS3 port. (The PS3 uses OpenGL ES, though the implementation is not ideal)
Instead, it loads shaders based off of XML files found in packages/core/data/postprocesseffects.erf. These do essentially the same thing - let's take a look at that file.You'll notice it's not very big. We're interested in da_fade.ffx.xml.
So, what can we gather from this file? Unfortunately a walkthrough is difficult, because it's an XML file, but bear with me as I hit the highlights:
target id="BackBufferCopy" width="BACKBUFFER_WIDTH" height="BACKBUFFER_HEIGHT" format="A8R8G8B8" shared="true"
--This is the "image" we are interested in - the back buffer contains the rendered frame
runtime id="WindowWidth" value="backbuffer_width"
runtime id="WindowHeight" value="backbuffer_height"
float id="BlurStrength" value="-3.5" range="-20.0 20.0" expose = "true" animatable="true"
-- These are some variables passed into the shader, which may be interesting in the future if we want to change the effect.
stage id="default" event="EndFrame"
-- this appears to indicate this effect should be applied after the frame is rendered
filter id="Radial Blur" library="DAEffects" material="GaussianBlur"
-- This is the first effect in our pass. It is what causes the edges of the screen to be blurred and the center to remain clear.
fBlurSize = BlurStrength
fWindowWidth = WindowWidth
fWindowHeight = WindowHeight
-- This are 'uniform' variables - for each pixel rendered they are exactly the same value, passed into the shader by the game.
Decal0 = BackBufferCopy
bind BACKBUFFER at 0
-- This is a texture. Most modern graphics cards can handle 16 or so. Our only texture here is the back buffer.
run semantic RadialBlur
-- This appears to run the shader "RadialBlur"
copy BACKBUFFER to BackBufferCopy filter NONE
-- Now thay our back buffer is blurred, we can apply the wavy distortion effect
filter id="Distortion" library="DAEffects" material="GaussianBlur"
Decal0 = BackBufferCopy
bind BACKBUFFER at 0
run semantic FadeDistortion
-- Our shader here is called "FadeDistortion"So what have we discovered? We know that this effect takes 2 passes: one for the radial blur, and one for the distortion.
We also know the names of the shaders used, so we should go look for them.
For your reference, the shaders are located in packages/core/data/shaders.erf. Go ahead and open it in the Toolkit and check out the list.You'll notice that they have two extensions: psh.fxo and vsh.fxo, for pixel shaders and vertex shaders, respectively. We are interested in fadedistortino.psh.fxo.
You'll probably notice that opening this results in garbage. That's because it's a pre-compiled shader - which is what fxo denotes.
Next time I'll show you how to get it back into a readable form. For now, let's try replacing it with something else.
We know that the game will provide a copy of the framebuffer at texture 0. So let's try to simply pass through that information.
That pixel shader looks like this:
sampler2D s0;
float4 main(float4 tex : TEXCOORD0) : COLOR0
{
return tex2D(s0, tex.xy);
}
Now, how does this work?sampler2D s0;This is a texture sampler. It reads from a 2-dimensional texture map based on coordinates. We will use this to read the back buffer.
float4 main(float4 tex : TEXCOORD0) : COLOR0Shaders need a "main" method - this is what is actually executed. Our main is defined as outputing a float4, which is a 4-component vector. At the end, you see : COLOR0, which indicates this output writes to the framebuffer color for this pixel. Our input is a float4 we're calling tex. This is being pulled from TEXCOORD0, which is the first set of texture coordinates provided to us.
return tex2D(s0, tex.xy);This simply reads the texture and returns the color.
Simple, right? One last thing - we need this data in a format the game can consume.
The shaders are already compiled, so we probably can't just stick this shader in the override folder.
Fortunately, the DirectX SDK includes a shader compiler, FXC.
FXC is pretty simple, you give it an input file, a profile and output info.
First, let's see what our shader looks like compiled. /T tells fxc that this is a pixel shader of version 2.0.
C:\\>fxc passthroughhlsl.fx /T ps_2_0
//
// Generated by Microsoft (R) HLSL Shader Compiler 9.26.952.2844
//
// fxc passthroughhlsl.fx /T ps_2_0
//
//
// Parameters:
//
// sampler2D s0;
//
//
// Registers:
//
// Name Reg Size
// ------------ ----- ----
// s0 s0 1
//
ps_2_0
dcl t0.xy //declare texcoord
dcl_2d s0 //declare sampler
texld r0, t0, s0 //read from texture
mov oC0, r0 //write to output color
// approximately 2 instruction slots used (1 texture, 1 arithmetic)
This looks good, so now let's compile it.C:\\>fxc passthroughhlsl.fx /T ps_2_0 /Fo "fadedistortion.psh.fxo" Microsoft (R) Direct3D Shader Compiler 9.26.952.2844 Copyright (C) Microsoft Corporation 2002-2009. All rights reserved. compilation succeeded; see C:\\fadedistortion.psh.fxo
Now we have our own fadedistortion.psh.fxo. Let's put that in the override folder and run the game.
If you start a new playthrough as a Mage you are almost immediately thrown into the Fade, so you can see if your effect works.

You may notice that this is a bit boring, so let's do something ridiculous:
sampler2D s0;
float4 main(float4 tex : TEXCOORD0) : COLOR0
{
float4 c;
c = tex2D(s0, tex.xy);
c.r = c.r * sin(tex.y*50);
c.g = c.g * cos(tex.y*50);
c.b = c.b * sin(tex.y*50);
return c;
}
C:\\>fxc wacky.fx /T ps_2_0 /Fo "fadedistortion.psh.fxo"
Microsoft (R) Direct3D Shader Compiler 9.26.952.2844
Copyright (C) Microsoft Corporation 2002-2009. All rights reserved.
compilation succeeded; see C:\\fadedistortion.psh.fxo
This gives you a weird banding look:
Next time we'll get into more interesting effects, and talk about how to take a look at the game's existing shaders to see how they work.
#6
Posté 18 novembre 2009 - 07:02
Seems really great so far. A while back I was looking if there were ways to tone down or remove the bloom effect without disabling all frame buffer effects through the menu option. I tried changing the relevant-looking settings in da_bloom.ffx.xml, but it never seemed to get picked up in the override, unlike your FXO files.
You mentioned the XML files in your post, so I'm curious if you've had better luck there than I have, or if I must be putting them in the wrong directory... or something else. I know that's a little vague, but mainly I'm just wondering if you've been able to change the XML files and have those work in the override - or if you only use them just as an interface for the FXO.
You mentioned the XML files in your post, so I'm curious if you've had better luck there than I have, or if I must be putting them in the wrong directory... or something else. I know that's a little vague, but mainly I'm just wondering if you've been able to change the XML files and have those work in the override - or if you only use them just as an interface for the FXO.
Modifié par FollowTheGourd, 18 novembre 2009 - 07:05 .
#7
Posté 18 novembre 2009 - 07:20
I haven't tried changing the xml files, actually. I would assume they just define uniforms that are passed to the shaders, but I'm not sure. There are also a bunch of shaders that look to be tied to specific maps, so they need to modified as well....
I'll rip that shader apart and see if there's an easy way to tone it down - it actually should be as simple as scaling down the inputs. Out of curiosity, how big were the changes you made? You might have to really adjust the values.
I'll rip that shader apart and see if there's an easy way to tone it down - it actually should be as simple as scaling down the inputs. Out of curiosity, how big were the changes you made? You might have to really adjust the values.
#8
Posté 18 novembre 2009 - 07:22
Shaders, part 2
I inquired about posting this and no one complained, so here goes:
This is the fade distortion shader disassembled.
This is DirectX assembly code, and can be really hard to follow if you
haven't spent a lot of time looking at similar things. I only have time for a quick
explanation right now, but hopefully I'll be able to go into detail about what is happening
later on in the week.
Basically, every line is a command that your graphics card will execute. This set of instructions
occurs once per pixel that is drawn on the screen. Finally, this image is combined with another image
that contains the UI and you get the actual game screen.
Something to keep in mind:
The format of these instructions is usually
destination, source
A basic intro to the instructions (add is pretty obvious):
As for variables - here we have registers, some are similar to a normal PC, others not so much.
Registers here are 4-component vectors, typically.
t0 is the first set of texture coordinates
s0 and s1 are texture samplers.
s0 is the framebuffer - the image we want to blur.
s1 appears to be the UI. I'm not sure if it's Scaleform that renders it into a separate image
or if that's just how the engine is designed. Seems like a waste of a pass to me, though.
r0-r2 are temporary storage.
c1 - c5 are constant values
The basis of this are the sincos commands, which are what is doing the work of generating the
"wavy" effect. Now, since the effect animates, the shader must also be given some sort of value
that changes every frame - that's the c0 value as far as I can tell.
I inquired about posting this and no one complained, so here goes:
This is the fade distortion shader disassembled.
ps_2_0 def c1, 0.048, 0.042, 1, 0.003 def c2, 39.992455, 0.5, 6.283185, -3.141593 def c3, 0.999811, 0.5, 0.002, 31.993963 def c4, -0.000002, -0.000022, 0.002604, 0.00026 def c5, -0.020833, -0.125, 1, 0.5 dcl_texcoord t0.xy dcl_2d s0 dcl_2d s1 texld r0, t0, s1 mov r0.xy, c1 mad r0.y, c0.x, r0.y, t0.y frc r0.y, r0.y mad r0.z, r0.y, c3.x, c3.y mad r0.y, r0.y, c2.x, c2.y frc r0.z, r0.z mad r0.z, r0.z, c2.z, c2.w sincos r1.x, r0.z, c4, c5 mul r0.z, r1.x, c1.w frc r0.y, r0.y mad r0.y, r0.y, c2.z, c2.w sincos r1.y, r0.y, c4, c5 mad r0.y, r1.y, c3.z, r0.z add r0.z, -r0.w, c1.z mul r1.x, r0.y, r0.z mad r0.x, c0.x, r0.x, t0.x frc r0.x, r0.x mad r0.y, r0.x, c3.x, c3.y mad r0.x, r0.x, c3.w, c3.y frc r0.y, r0.y mad r0.y, r0.y, c2.z, c2.w sincos r2.y, r0.y, c4, c5 mul r0.y, r2.y, c1.w frc r0.x, r0.x mad r0.x, r0.x, c2.z, c2.w sincos r2.x, r0.x, c4, c5 mad r0.x, r2.x, c3.z, r0.y mul r1.y, r0.z, r0.x add r0.xy, r1, t0 texld r0, r0, s0 texld r1, t0, s0 lrp r2.xyz, c2.y, r1, r0 mov r2.w, c1.z mov oC0, r2 end
This is DirectX assembly code, and can be really hard to follow if you
haven't spent a lot of time looking at similar things. I only have time for a quick
explanation right now, but hopefully I'll be able to go into detail about what is happening
later on in the week.
Basically, every line is a command that your graphics card will execute. This set of instructions
occurs once per pixel that is drawn on the screen. Finally, this image is combined with another image
that contains the UI and you get the actual game screen.
Something to keep in mind:
The format of these instructions is usually
destination, source
mov oC0, r2This places the value in r2 in the output color.
A basic intro to the instructions (add is pretty obvious):
- def - Defines a constant value
- dcl_texcoord - Declares texture coordinates
- dcl_2d - Declares a texture sampler
- texld - Reads from a texture
- mov - Moves a value from one place to another
- mad - Multiply and add ((x * y) + z)
- frc - Get the fractional component only
- sincos - Sin and cosine, input in radians
- lrp - Linear Interpolation: a(x0) + (1-a)x1
As for variables - here we have registers, some are similar to a normal PC, others not so much.
Registers here are 4-component vectors, typically.
t0 is the first set of texture coordinates
s0 and s1 are texture samplers.
s0 is the framebuffer - the image we want to blur.
s1 appears to be the UI. I'm not sure if it's Scaleform that renders it into a separate image
or if that's just how the engine is designed. Seems like a waste of a pass to me, though.
r0-r2 are temporary storage.
c1 - c5 are constant values
The basis of this are the sincos commands, which are what is doing the work of generating the
"wavy" effect. Now, since the effect animates, the shader must also be given some sort of value
that changes every frame - that's the c0 value as far as I can tell.
#9
Posté 18 novembre 2009 - 07:26
put it on the wiki!
#10
Posté 18 novembre 2009 - 08:51
So it is standard HLSL?
#11
Posté 18 novembre 2009 - 08:56
Well, it might have been at one time. But the shipping files are compiled into bytecode. I'll try translating back to HLSL if I have a chance.
#12
Posté 18 novembre 2009 - 08:59
Ah np, I thought you were working Top down, no need to go out of your way to cover my curiosity, unless you feel like it that is
.
#13
Posté 18 novembre 2009 - 02:48
Driveninhifi wrote...
I haven't tried changing the xml
files, actually. I would assume they just define uniforms that are
passed to the shaders, but I'm not sure. There are also a bunch of
shaders that look to be tied to specific maps, so they need to modified
as well....
I'll rip that shader apart and see if there's an
easy way to tone it down - it actually should be as simple as scaling
down the inputs. Out of curiosity, how big were the changes you made?
You might have to really adjust the values.
I believe I tried things like setting the glow intensity to zero, and even just blanking the file to see if that would at least cause things to get messed up... but I could never cause any change that way.
Anyway, thanks for planning on looking into it. If you do manage to tone it down, I think you'll be doing a favour to eyeballs everywhere.
#14
Posté 18 novembre 2009 - 07:45
I'd be curious to know if removing the postprocesseffects.erf file breaks the game completely. It could be ignored or there could be references elsewhere that also need to be changed. It might be easier to just rewrite the shader.
And yeah, I do intend to translate the fade shader into a more human readable form if I get the time. Though if the bloom shader is more simple I may start with that one.
And yeah, I do intend to translate the fade shader into a more human readable form if I get the time. Though if the bloom shader is more simple I may start with that one.
#15
Posté 18 novembre 2009 - 07:54
Hmmm...interesting, very interesting. I am almost tempted to start adding some shader support to my DA model viewer (currently it just displays the plain diffuse texture) .... but - ugh.... it might be a bit too much work.
Very interesting posts from you, though.
Very interesting posts from you, though.
#16
Posté 19 novembre 2009 - 12:05
If you can read out the tangent, bitangent and normal information from the model, it's really easy to apply normal mapping. That's kind of outside the scope of what I'm attempting to detail in this post, but I'd be happy to chat with you about it if you are interested.





Retour en haut






