# CMSC 435/634: Introduction to Computer Graphics

## Assignment 6 Shading Due May 13, 2008

### The Assignment

For this assignment, you will use OpenGL or XNA to extend your Assignment 2 (or the Assignment 2 sample code from the TA) to include procedural bark on the tree. Your bark should compute both color and bump or normal-map based lighting in a fragment/pixel shader. You may choose to also create a custom vertex shader, but this is not necessary for the assignment. You may also use color or noise textures within your shader.

For information on how to load and activate a shader, see the GPU shading class slides for OpenGL, or the XNA samples for XNA. You may use code from any of these sources as part of your assignment.

Some of the grade will be awarded for creativity, originality and effort.

### Extra credit

Try to mimic a specific species of real tree as closely as possible. Include a JPEG photo of the tree you have tried to match along with your completed assignment.

Steve May's RMan Notes, from a course at Ohio State, is focused on RenderMan, but has many helpful tips on shader development strategies.

To create the bumps, you will need to construct a local tangent frame for the surface (sometimes called a TBN matrix for Tangent/Bitangent/Normal). It is easy to get the per-pixel normal. You will need to pass in the axis of the branch to align the direction of your bark. The two tangents can be computed by one of the usual orthonormalization procedures (cross products or Gram-Schmidt). These three vectors then make a 3x3 matrix to transform from object space to tangent space.

Here is a noise texture you can use, and some code to use it based on Olano, "Modified Noise for Evaluation on Graphics Hardware," In Proceedings of ACM SIGGRAPH/Eurographics Graphics Hardware 2005:

GLSL HLSL
uniform sampler2D ntex;

const float NTmod = 61.; // modulus in random hash
const vec4 NTcoef = vec4(13.,17.,7.,1.); // hash offsets

// noise with 2D input
float noise(vec2 p)
{
return texture2D(ntex, p/NTmod).r*2.-1.;
}

// noise with 3D input
float noise(vec3 p)
{
// input cell, location in cell, and fade blend function
float i = floor(p.z), f = fract(p.z), sf = (3.-2.*f)*f*f;

// hash z & z+1 for noise offsets
vec2 h = mod(NTcoef.z*(i+vec2(0,1.)),NTmod);
h = mod(h*h,NTmod);

// lookup noise and blend slices
vec2 g0 = texture2D(ntex, (p.xy+vec2(0,h.x))/NTmod).xy*2.-1.;
vec2 g1 = texture2D(ntex, (p.xy+vec2(0,h.y))/NTmod).xy*2.-1.;
return mix(g0.r+g0.g*f, g1.r+g1.g*(f-1.), sf);
}

texture ntex;

float NTmod = 61.; // modulus in random hash
float4 NTcoef = float4(13.,17.,7.,1.); // hash offsets

// noise with 2D input
float noise(float2 p)
{
return tex2D(TextureSampler, p/NTmod).r*2.-1.;
}

// noise with 3D input
float noise(float3 p)
{
// input cell, location in cell, and fade blend function
float i = floor(p.z), f = frac(p.z), sf = (3.-2.*f)*f*f;

// hash z & z+1 for noise offsets
float2 h = fmod(NTcoef.z*(i+float2(0,1.)),NTmod);
h = fmod(h*h,NTmod);

// lookup noise and blend slices
float2 g0 = tex2D(TextureSampler, (p.xy+float2(0,h.x))/NTmod).xy*2.-1.;
float2 g1 = tex2D(TextureSampler, (p.xy+float2(0,h.y))/NTmod).xy*2.-1.;
return (g0.r+g0.g*f)*(1-sf) + (g1.r+g1.g*(f-1.))*sf;
}