Gradient-generating shader with arbitrary color components - hlsl

The task is: shader takes in a constant color, then generates pixel colors according to their positions by replacing two of four color components (RGBA) with texture coordinates.
With hardcoded component set it will be like:
float4 inputColor : register(c0);
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 color = 0;
color.a = inputColor.a;
color.r = inputColor.r;
color.g = uv.x;
color.b = uv.y;
return color;
}
Now I'd like to pass in a parameter(s) specifying which components should be replaced with uv.x and uv.y. Let's say inputColor has -1 and -2 in these components. Or there are uint xIndex and yIndex parameters specifying positions in vector4 to be replaced. HLSL does not allow "color[xIndex] = uv.x".
Currently I've done that in ugly way with a bunch of if-else. But I feel like there is some cross-product or matrix multiplication solution. Any ideas?

You could work with two additional vectors as channelmasks. It works like indexing, but with vector operators.
float4 inputColor : register(c0);
float4 uvx_mask : register(c1); //e.g. (0,0,1,0)
float4 uvy_mask : register(c2); // e.g. (0,0,0,1)
float4 main(float2 uv : TEXCOORD) : COLOR
{
float4 color = 0;
// replacing uvx channel with uv.x
color = lerp(inputColor, uv.x * uvx_mask, uvx_mask);
// replacing uvy channel with uv.y
color = lerp(color , uv.y * uvy_mask, uvy_mask);
return color; //in this example (inputColor.r, inputColor.g, uv.x, uv.y)
}
If you need even the last bit of performance you could work alternative with the preprocessor (#define, #ifdef) to build the right code on demand.

Related

How to use pixel shader to render material from a Wavefront Obj file?

Some 3d meshes that get exported to Wavefront.obj format usually come with a .mtl file that has additional data to the texture it uses and its materials, when exported from Blender they always come with Ambient, Diffuse, Specular, and Emissive RGB data as part of its material, but I'm not sure how I can use this data in the pixel shader and get the right color output.
I would appreciate it if anyone can explain to me how to use these materials and any code sample would be very welcome.
Traditional materials and lighting models use "Ambient", "Diffuse", "Specular", and "Emissive" colors which is why you find those in Wavefront OBJ files. These can often be replaced or used in multiplicative conjunction with texture colors.
The (now defunct) XNA Game Studio product did a good job of providing simple 'classic' shaders in the BasicEffect "Stock Shaders". I use them in the DirectX Tool Kit for DX11 and DX12.
Take a look at BasicEffect.fx for a traditional material pixel shader. If you are looking mostly for pixel-shader handling, that's "per-pixel lighting" as opposed to "vertex lighting" which was more common back when we had less powerful GPUs.
Here's a 'inlined' version so you can follow it all in one place:
struct VSInputNmTx
{
float4 Position : SV_Position;
float3 Normal : NORMAL;
float2 TexCoord : TEXCOORD0;
};
Texture2D<float4> Texture : register(t0);
sampler Sampler : register(s0);
cbuffer Parameters : register(b0)
{
float4 DiffuseColor : packoffset(c0);
float3 EmissiveColor : packoffset(c1);
float3 SpecularColor : packoffset(c2);
float SpecularPower : packoffset(c2.w);
float3 LightDirection[3] : packoffset(c3);
float3 LightDiffuseColor[3] : packoffset(c6);
float3 LightSpecularColor[3] : packoffset(c9);
float3 EyePosition : packoffset(c12);
float3 FogColor : packoffset(c13);
float4 FogVector : packoffset(c14);
float4x4 World : packoffset(c15);
float3x3 WorldInverseTranspose : packoffset(c19);
float4x4 WorldViewProj : packoffset(c22);
};
struct VSOutputPixelLightingTx
{
float2 TexCoord : TEXCOORD0;
float4 PositionWS : TEXCOORD1;
float3 NormalWS : TEXCOORD2;
float4 Diffuse : COLOR0;
float4 PositionPS : SV_Position;
};
// Vertex shader: pixel lighting + texture.
VSOutputPixelLighting VSBasicPixelLightingTx(VSInputNmTx vin)
{
VSOutputPixelLighting vout;
vout.PositionPS = mul(vin.Position, WorldViewProj);
vout.PositionWS.xyz = mul(vin.Position, World).xyz;
// ComputeFogFactor
vout.PositionWS.w = saturate(dot(vin.Position, FogVector));
vout.NormalWS = normalize(mul(vin.Normal, WorldInverseTranspose));
vout.Diffuse = float4(1, 1, 1, DiffuseColor.a);
vut.TexCoord = vin.TexCoord;
return vout;
}
struct PSInputPixelLightingTx
{
float2 TexCoord : TEXCOORD0;
float4 PositionWS : TEXCOORD1;
float3 NormalWS : TEXCOORD2;
float4 Diffuse : COLOR0;
};
// Pixel shader: pixel lighting + texture.
float4 PSBasicPixelLightingTx(PSInputPixelLightingTx pin) : SV_Target0
{
float4 color = Texture.Sample(Sampler, pin.TexCoord) * pin.Diffuse;
float3 eyeVector = normalize(EyePosition - pin.PositionWS.xyz);
float3 worldNormal = normalize(pin.NormalWS);
ColorPair lightResult = ComputeLights(eyeVector, worldNormal, 3);
color.rgb *= lightResult.Diffuse;
// AddSpecular
color.rgb += lightResult.Specular * color.a;
// ApplyFog (we passed fogfactor in via PositionWS.w)
color.rgb = lerp(color.rgb, FogColor * color.a, pin.PositionWS.w);
return color;
}
Here is the helper function ComputeLights which implements a Blinn-Phong reflection model for the specular highlight.
struct ColorPair
{
float3 Diffuse;
float3 Specular;
};
ColorPair ComputeLights(float3 eyeVector, float3 worldNormal, uniform int numLights)
{
float3x3 lightDirections = 0;
float3x3 lightDiffuse = 0;
float3x3 lightSpecular = 0;
float3x3 halfVectors = 0;
[unroll]
for (int i = 0; i < numLights; i++)
{
lightDirections[i] = LightDirection[i];
lightDiffuse[i] = LightDiffuseColor[i];
lightSpecular[i] = LightSpecularColor[i];
halfVectors[i] = normalize(eyeVector - lightDirections[i]);
}
float3 dotL = mul(-lightDirections, worldNormal);
float3 dotH = mul(halfVectors, worldNormal);
float3 zeroL = step(0, dotL);
float3 diffuse = zeroL * dotL;
float3 specular = pow(max(dotH, 0) * zeroL, SpecularPower) * dotL;
ColorPair result;
result.Diffuse = mul(diffuse, lightDiffuse) * DiffuseColor.rgb + EmissiveColor;
result.Specular = mul(specular, lightSpecular) * SpecularColor;
return result;
}
These BasicEffect shaders don't make use of ambient color, but you could modify them to do so if you wanted. All ambient color does is provide a 'minimum color value' that's independent of dynamic lights.
Note that there also some unofficial Physically-Based Rendering (PBR) materials extension in some Wavefront OBJ files. See Extending Wavefront MTL for Physically-Based. More modern geometry formats like glTF assume PBR materials properties which is things like an albedo texture, normal texture, roughness/metalness texture, etc..

How to sample two textures onto the same object?

I have to reproduce an effect which consists of combining two textures (tiles + coin) to achieve the following:
The best result I achieved so far:
Visual Studio Solution to reproduce the problem
The link above will take you to the project and here what I tried to do in the pixel shader:
float4 PS(PS_INPUT input) : SV_Target
{
float4 color1;
float4 color2;
float4 blendColor;
// Get the pixel color from the first texture.
color1 = texTile.Sample(samLinear, input.Tex) * vMeshColor;
// Get the pixel color from the second texture.
color2 = texCoin.Sample(samLinear, input.Tex) * vMeshColor;
// Blend the two pixels together and multiply by the gamma value.
blendColor = color1 * color2;
// Saturate the final color.
blendColor = saturate(blendColor);
return blendColor;
}
But it does not seem the right way of doing this.
Which aproach should I take to have the expected result?
Well firstly you are blending them but not blending using an alpha-mask, when the example image seems to have been blended with an alpha-mask.
An example could be something like below; provided the coin has got an alpha-channel.
(Otherwise you'll have to calculate an alpha or add one in an image editing software.
float3 blend(float4 CoinTex, float3 GridTex)
{
// Inverse of alpha, to get the area around the coin
// Alpha spans from [0,1] so the expression below would suffice
float1 inverseAlpha = (1 - CoinTex.a);
float3 blendedTex = 0;
// If-else that is evaluated to decide how they'll be overlayed
if (inverseAlpha > 0.0 ){
blendedTex = GridTex.rgb;
} else {blendedTex = CoinTex.rgb;}
return blendedTex;
}

How to achieve left to right color gradient for non Standard shapes without using Texture coordinates in hlsl?

I have searched a lot on Google and most of examples achieving gradient using texture coordinates. But I don't have texture coordinates with me. I am working on 3D text on which I want to apply gradient color. Is it possible? If yes, how? Is it necessary to have texture coordinates for obtaining color gradient?
Following is the part of my hlsl shader file :
struct VS_INPUT
{
float3 Pos : POSITION;
float3 Norm : NORMAL;
};
struct PS_INPUT
{
float4 Pos : SV_POSITION;
float3 WorldNorm : TEXCOORD0;
float3 CameraPos : TEXCOORD1;
float3 WorldPos : TEXCOORD2;
};
//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
PS_INPUT VS( VS_INPUT input )
{
PS_INPUT output = (PS_INPUT)0;
float4 worldPos = mul( float4(input.Pos,1), World );
float4 cameraPos = mul( worldPos, View );
output.WorldPos = worldPos;
output.WorldNorm = normalize(mul( input.Norm, (float3x3)World ));
output.CameraPos = cameraPos;
output.Pos = mul( cameraPos, Projection );
return output;
}
//--------------------------------------------------------------------------------------
// Pixel Shader Without Light
//--------------------------------------------------------------------------------------
float4 PS( PS_INPUT input) : SV_Target
{
float4 finalColor = {1.0f, 0.0f, 0.0f, 1.0f};
return finalColor;
}
//--------------------------------------------------------------------------------------
technique10 Render
{
pass P0
{
SetVertexShader( CompileShader( vs_4_0_level_9_1, VS() ) );
SetGeometryShader( NULL );
SetPixelShader( CompileShader( ps_4_0_level_9_1, PS() ) );
}
}
You don't need texture coordinates, because they are only one possible method (the most flexible) to save needed information about how your gradient should look like, as such origin, direction and length.
To have a gradient from left to right of your 3D-Text, you need to know where is left and right in your shader to take the appropriate color. I assume that your text is changing and such dynamically, so you need to transport this information into the shader, which either can be placed into the vertices directly by texture coordinates or with a constant buffer. Last method would only work if you draw at most one text per drawcall, because the gradient data is persistent over the whole drawing of all triangles in your drawcall.
If your situation is more special, as like your text is axis-aligned, you could take this axis and the worldposition in your pixelshader to determine the relative position for your gradient, but this method makes many assumptions as you still need the left and right maximum of your text.

how do i create 3d bounding box? (Direct 3d 11, HLSL)

I am using direct 3d for the first time and i am looking for a way to represent bounding boxes (rectangles/cylinders/spheres). How do i outline the bounding box? Is this a shader, or do i use somthing else to create an outlined shape?
chasester
The simplest way is to use a line list to create a unit wireframe bounding volume.
using a transform matrix, you can scale, translate or rotate the volume around any object in your 3D world.
accomplishing this requires 3 main parts:
A constant VB and IB with the linelist verts set up (remember it needs to be centered around the origin, and have a unit length of 1), plus a VB for per-instance data.
An input layout that takes in a transform matrix as a per instance member.
A vertex shader that applies the transform matrix to each vertex of the cube. (the pixel shader need only output the color as is supplied).
(It should be noted that the same principle applies to spheres, hulls etc. as well)
The shaders I use look like this:
struct WIRE_VS_INPUT
{
float3 Position : POSITION;
float4 Color : COLOR0;
float3 Transform : TRANSFORM;
float3 Size : SIZE;
};
struct WIRE_VS_OUTPUT
{
float4 Position : SV_POSITION;
float4 Color : COLOR0;
};
WIRE_VS_OUTPUT WireBoxVS( WIRE_VS_INPUT Input )
{
WIRE_VS_OUTPUT Output;
Output.Position = mul(float4((Input.Position * Input.Size) + Input.Transform,1),g_mWorldViewProjection);
Output.Color = Input.Color;
return Output;
}
float4 WireBoxPS( WIRE_VS_OUTPUT Input ) : SV_TARGET
{
return Input.Color;
}
The cube VB & IB setup:
const DirectX::XMFLOAT3 kWireCube[] =
{
DirectX::XMFLOAT3(-1,-1,-1),
DirectX::XMFLOAT3(1,-1,-1),
DirectX::XMFLOAT3(1,1,-1),
DirectX::XMFLOAT3(-1,1,-1),
DirectX::XMFLOAT3(-1,-1,1),
DirectX::XMFLOAT3(1,-1,1),
DirectX::XMFLOAT3(1,1,1),
DirectX::XMFLOAT3(-1,1,1)
};
const WORD kWireCubeIndices[] =
{
0,1,
1,2,
2,3,
3,0,
4,5,
5,6,
6,7,
7,4,
0,4,
1,5,
2,6,
3,7
};

Getting the color of a vertex in HLSL?

I have the following vertex and pixel shaders:
struct VS_INPUT
{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
float4 Color : TEXCOORD1;
};
struct VS_OUTPUT
{
float4 Position : POSITION0;
float4 Color : COLOR0;
float2 TexCoord : TEXCOORD0;
};
float4x4 projview_matrix;
VS_OUTPUT vs_main(VS_INPUT Input)
{
VS_OUTPUT Output;
Output.Position = mul(Input.Position, projview_matrix);
Output.Color = Input.Color;
Output.TexCoord = Input.TexCoord;
return Output;
}
px
texture tex;
sampler2D s = sampler_state {
texture = <tex>;
};
float4 ps_main(VS_OUTPUT Input) : COLOR0
{
float4 pixel = tex2D(s, Input.TexCoord.xy);
return pixel;
}
This is for a 2d game. The vertices of the quads contain tinting colors that I want to use to tint the bitmap. How can I obtain the color of the current vertex so I can multiply it in the pixel shader by the current pixel color?
Thanks
In your pixel shader, do:
float4 pixel = tex2D(s, Input.TexCoord.xy) * Input.Color;
The Input.Color value will be linearly interpreted across your plane for you, just like Input.TexCoord is. To blend two color vectors together, you simply multiply them together. It may also be advisable to do:
float4 pixel = tex2D(s, Input.TexCoord.xy) * Input.Color;
pixel = saturate(pixel);
The saturate() function will clip each RGB value in your color in the range of 0.0 to 1.0, which may avoid any possible display artifacts.