EDIT: I made some more changes to this shader. You can now control the depth in real time instead of having to edit it in the material script.
#####
I came across this example while looking for some info on parallax occlusion mapping. In my opinion a POM shader is better, but this one is pretty good too.
I thought it would be a good idea to post it anyways. Just in case somebody might have a use for it.
This requires NVIDIA's Cg-toolkit to be installed on your computer. Here's the link to the toolkit information.
https://developer.nvidia.com/cg-toolkit
The download link is at the bottom of that page.
If you intend to release your apps with this shader. Make sure you include the cg.dll with your app.
A few things about the shader.
It was originally a free example from NVIDIA. Someone from the OGRE site ported it over to cg. I forgot the OGRE link, but I'll post it as soon as I find it.
I did make some small changes on it. It was originally set up for one material. I changed it so you can have multiple materials in the same script.
You can use this for whatever purpose you want. There are no copyright restrictions that I'm aware of.
It's main purpose is to give a 2D texture more depth. Which gives a more realistic appearance.
It supports diffuse,specular, and ambient lighting.
It has shadow support for the texture itself. Which gives it a better depth feeling.
A warning.
This shader along with any shader of this caliber requires quite a bit of power.
It's something that you will have to experiment with to find the proper balance for your app.
Use it too much and your FPS will surely drop. Unless you have a beast of a graphics card.
Now then here's a picture showing it in action.
These next two pictures are the required materials for the shader. One is the texture you want your entity to use.
The other is the Normal Height Map. Which is used for the calculations.
Save them exactly as stated. Otherwise the shader won't be able to find them.
Also don't forget where you save them. You will need to set the path later on in the PB code.
DiffuseMap.png
You will need to load this next one in a program that supports alpha. I personally prefer using GIMP, but Photoshop will work too.
Make sure this one is saved as a png because jpg doesn't support alpha.
EDIT: I changed this Normal Height Image to a different one.
The original had a defect. So, it would show a little gray splotch in the center of each square.
This end result may look slightly different from the original because I smoothed the map a little, but it's better in my opinion.
NormalHeight.png
The GIMP has a nice normal map filter that you can download, but I forgot where the link was. I'll post the link as soon as I find it.
EDIT: Bananenfreak found this link to a NormalMap Plugin for GIMP
http://code.google.com/p/gimp-normalmap/
Now the code.
There are 3 files and the PB code itself. The three files are the cg, material, and program.
Same as the pictures above. Make sure they are saved exactly as stated extension and all.
I mainly use notepad, but use whatever works for you.
First the cg file.
relief_map.CG
Code: Select all
/*********************************************************************NVMH3****
*******************************************************************************
$Revision: #4 $
Copyright NVIDIA Corporation 2008
TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED
*AS IS* AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL NVIDIA OR ITS SUPPLIERS
BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES
WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS,
BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, OR ANY OTHER PECUNIARY
LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF
NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
% This material shows and compares results from four popular and
% advanced schemes for emulating displaement mapping. They are:
% Relief Mapping, Parallax Mapping, Normal Mapping, and Relief
% Mapping with Shadows. Original File by Fabio Policarpo.
keywords: material bumpmap
date: 071005
Note: Strong discontinuties in the model geometric normal (e.g., very sharp
differences from the normals in two or more parts of the same triangle)
can cause unusual overall light-attentuation errors. Re-normalizing the
rasterized normals in the fragment shader can correct this, but the case
was considered rare enough that these extra steps were eliminated for
code efficiency. If you see off lighting near sharp model edges, just
normalize "IN.normal" in the calculation of the varible "att" (shared
by all techniques).
To learn more about shading, shaders, and to bounce ideas off other shader
authors and users, visit the NVIDIA Shader Library Forums at:
http://developer.nvidia.com/forums/
*******************************************************************************
******************************************************************************/
// Modified for OGRE by mkultra333, 090529
// Got rid of BINORMAL input, now computed in vertex program.
// Changed "- tNorm.y*IN.binormal" to "+ tNorm.y*IN.binormal" in fragment program.
// Removed WorldITXf, WorldXf and ViewIXf 4x4 matrixes that weren't used.
/********** CONNECTOR STRUCTURES *****************/
struct AppVertexData {
float4 pos : POSITION;
float4 color : COLOR0;
float3 normal : NORMAL; // expected to be normalized
float2 txcoord : TEXCOORD0;
float3 tangent : TANGENT0; // pre-normalized
};
struct VertexOutput {
float4 hpos : POSITION;
float2 UV : TEXCOORD0;
float3 vpos : TEXCOORD1;
float3 tangent : TEXCOORD2;
float3 binormal : TEXCOORD3;
float3 normal : TEXCOORD4;
float4 lightpos : TEXCOORD5;
float4 color : COLOR0;
};
/*** SHADER FUNCTIONS **********************************************/
VertexOutput view_spaceVS(AppVertexData IN,
uniform float4x4 WvpXf,
uniform float4x4 ViewXf,
uniform float4x4 WorldViewXf,
uniform float TileCount,
uniform float3 LampPos
) {
VertexOutput OUT = (VertexOutput)0;
// isolate WorldViewXf rotation-only part
float3x3 modelViewRotXf;
modelViewRotXf[0] = WorldViewXf[0].xyz;
modelViewRotXf[1] = WorldViewXf[1].xyz;
modelViewRotXf[2] = WorldViewXf[2].xyz;
float4 Po = float4(IN.pos.xyz,1.0);
OUT.hpos = mul(WvpXf,Po);
// vertex position in view space (with model transformations)
OUT.vpos = mul(WorldViewXf,Po).xyz;
// light position in view space
float4 Lw = float4(LampPos.xyz,1); // this point in world space
OUT.lightpos = mul(ViewXf,Lw); // this point in view space
// Calculate the binormal (NB we assume both normal and tangent are
// already normalised)
// NB looks like nvidia cross params are BACKWARDS to what you'd expect
// this equates to NxT, not TxN
float3 binormal = cross(IN.tangent, IN.normal);
// tangent space vectors in view space (with model transformations)
OUT.tangent = mul(modelViewRotXf,IN.tangent);
OUT.binormal = mul(modelViewRotXf,binormal);
OUT.normal = mul(modelViewRotXf,IN.normal);
// copy color and texture coordinates
OUT.color = IN.color; // currently ignored by all techniques
OUT.UV = TileCount * IN.txcoord.xy;
return OUT;
}
//// ray-intersect functions for relief mapping //////////
float ray_intersect_rm( // use linear and binary search
in sampler2D reliefmap,
in float2 dp,
in float2 ds)
{
const int linear_search_steps=40;
// current size of search window
float size = 1.0/linear_search_steps;
// current depth position
float depth = 0.0;
// search front to back for first point inside object
for( int i=0;i<linear_search_steps-1;i++ ) {
float4 t = tex2D(reliefmap,dp+ds*depth);
if (depth<t.w)
depth += size;
}
const int binary_search_steps=5;
// recurse around first point (depth) for closest match
for( int ii=0;ii<binary_search_steps;ii++ ) {
size*=0.5;
float4 t = tex2D(reliefmap,dp+ds*depth);
if (depth<t.w)
depth += (2*size);
depth -= size;
}
return depth;
}
float ray_intersect_rm_lin( // only linear search for shadows
in sampler2D reliefmap,
in float2 dp,
in float2 ds)
{
const int linear_search_steps=40;
// current size of search window
float size = 1.0/linear_search_steps;
// current depth position
float depth = 0.0;
// search front to back for first point inside object
for( int i=0;i<linear_search_steps-1;i++ ) {
float4 t = tex2D(reliefmap,dp+ds*depth);
if (depth<t.w)
depth += size;
}
return depth;
}
float4 relief_mapPS(VertexOutput IN,
uniform float Depth,
uniform float3 SurfaceColor,
uniform sampler2D ColorSampler : register(s1),
uniform sampler2D ReliefSampler : register(s0),
uniform float PhongExp,
uniform float3 SpecColor,
uniform float3 AmbiColor
) : COLOR
{
// ray intersect in view direction
float3 p = IN.vpos;
float3 Vn = normalize(p);
float a = dot(IN.normal,-Vn);
float3 s = float3(dot(Vn,IN.tangent.xyz), dot(Vn,IN.binormal.xyz), a);
s *= Depth/a;
float2 ds = s.xy;
float2 dp = IN.UV;
float d = ray_intersect_rm(ReliefSampler,dp,ds);
// get rm and color texture points
float2 uv = dp+ds*d;
float3 texCol = tex2D(ColorSampler,uv).xyz;
float3 tNorm = tex2D(ReliefSampler,uv).xyz - float3(0.5,0.5,0.5);
tNorm = normalize(tNorm.x*IN.tangent +
tNorm.y*IN.binormal +
tNorm.z*IN.normal);
// compute light direction
p += Vn*d/(a*Depth);
float3 Ln = normalize(p-IN.lightpos.xyz);
// compute diffuse and specular terms
float att = saturate(dot(-Ln,IN.normal));
float diff = saturate(dot(-Ln,tNorm));
float spec = saturate(dot(normalize(-Ln-Vn),tNorm));
spec = pow(spec,PhongExp);
// compute final color
float3 finalcolor = AmbiColor*texCol +
att*(texCol*SurfaceColor*diff+SpecColor*spec);
return float4(finalcolor.rgb,1.0);
}
float4 relief_map_shadowsPS(VertexOutput IN,
uniform float Depth,
uniform float3 SurfaceColor,
uniform sampler2D ColorSampler : register(s1),
uniform sampler2D ReliefSampler : register(s0),
uniform float PhongExp,
uniform float3 SpecColor,
uniform float3 AmbiColor
) : COLOR
{
// ray intersect in view direction
float3 p = IN.vpos;
float3 Vn = normalize(p);
float a = dot(IN.normal,-Vn);
float3 s = float3(dot(Vn,IN.tangent.xyz), dot(Vn,IN.binormal.xyz), a);
s *= Depth/a;
float2 ds = s.xy;
float2 dp = IN.UV;
float d = ray_intersect_rm(ReliefSampler,dp,ds);
// get rm and color texture points
float2 uv = dp+ds*d;
float3 texCol = tex2D(ColorSampler,uv).xyz;
float3 tNorm = tex2D(ReliefSampler,uv).xyz - float3(0.5,0.5,0.5);
tNorm = normalize(tNorm.x*IN.tangent +
tNorm.y*IN.binormal +
tNorm.z*IN.normal);
// compute light direction
p += Vn*d/(a*Depth);
float3 Ln = normalize(p-IN.lightpos.xyz);
// compute diffuse and specular terms
float att = saturate(dot(-Ln,IN.normal));
float diff = saturate(dot(-Ln,tNorm));
float spec = saturate(dot(normalize(-Ln-Vn),tNorm));
// ray intersect in light direction
dp+= ds*d;
a = dot(IN.normal,-Ln);
s = float3(dot(Ln,IN.tangent.xyz),dot(Ln,IN.binormal.xyz),a);
s *= Depth/a;
ds = s.xy;
dp -= ds*d;
float dl = ray_intersect_rm_lin(ReliefSampler,dp,s.xy);
if (dl<d-0.05) { // if pixel in shadow
diff *= dot(AmbiColor.xyz,float3(1.0,1.0,1.0))*0.333333;
spec = 0;
}
spec = pow(spec,PhongExp);
// compute final color
float3 finalcolor = AmbiColor*texCol +
att*(texCol*SurfaceColor*diff+SpecColor*spec);
return float4(finalcolor.rgb,1.0);
}
relief_map.MATERIAL
Code: Select all
abstract material ReliefTemplate
{
technique
{
pass
{
vertex_program_ref view_spaceVS
{
param_named_auto WvpXf worldviewproj_matrix
param_named_auto ViewXf view_matrix
param_named_auto WorldViewXf worldview_matrix
param_named TileCount float $Tile
param_named_auto LampPos light_position 0
}
fragment_program_ref relief_map_shadowsPS
{
//param_named Depth float $Depth ####This is now commented out because the depth has custom control####
param_named_auto Depth custom 1
param_named_auto SurfaceColor light_diffuse_colour 0
param_named PhongExp float $PhongExp
param_named_auto SpecColor light_specular_colour 0
param_named_auto AmbiColor ambient_light_colour 0
}
texture_unit NormalMap
{
texture $NormalHeight_map
filtering anisotropic
max_anisotropy 1
}
texture_unit DiffuseMap
{
texture $Texture_Map
filtering anisotropic
max_anisotropy 1
}
}
}
}
material MaterialOne : ReliefTemplate
{
set $Tile 1
//set $Depth 0.09 ####This is now commented out because the depth has custom control####
set $PhongExp 64.0
set $Texture_Map DiffuseMap.png
set $NormalHeight_map NormalHeight.png
}
I'll tell you what each one means for future reference.
Do not use this code because it will not run. It's just for an example.
Code: Select all
material MaterialOne : ReliefTemplate #### MaterialOne is the name of your material that you will call in PB. ReliefTemplate is the template for your
#### material. So in other words it takes your variables that you will set below and uses them in the template material
#### above.
{
set $Tile 1 #### $Tile is how many times the texture is repeated. So, if you set it to 2 it will display your texture twice, but
#### they will be half the size.
set $Depth 0.05 #### $Depth is for how much depth you want. The better the Normal Height map the better the depth quality.
set $PhongExp 64.0 #### $PhongExp adjusts your light. The greater the number the smaller the light.
set $Texture_Map DiffuseMap.png #### $Texture_Map is your texture variable. Set DiffuseMap.png to whatever your texture is named.
set $NormalHeight_map NormalHeight.png #### $NormalHeight_map is your Normal Height variable. Set NormalHeight.png to whatever your Normal Height
#### Map is named.
}
relief_map.PROGRAM
Code: Select all
vertex_program view_spaceVS cg
{
source relief_map.cg
entry_point view_spaceVS
profiles arbvp1 vs_3_0 vp40
}
fragment_program relief_mapPS cg
{
source relief_map.cg
entry_point relief_mapPS
profiles ps_3_0 arbfp1 fp40
}
fragment_program relief_map_shadowsPS cg
{
source relief_map.cg
entry_point relief_map_shadowsPS
profiles ps_3_0 arbfp1 fp40
}
All that's left is setting up the code to run this.
Don't forget to set the paths to the files and pictures.
Code: Select all
UsePNGImageDecoder()
UsePNGImageEncoder()
;####You must Enable CG When Using A CG Shader.####
InitEngine3D(#PB_Engine3D_EnableCG | #PB_Engine3D_DebugLog)
InitSprite()
InitKeyboard()
InitMouse()
;#### (#Depth) Handle Must Equal 1 Because That is What It Was Set To In Script.####
Enumeration 1
#Depth
#MainWindow
#FPS
#Instructions
#Font
#Font2
#Sphere
#Camera
#Light
#ReliefMat
#TextureMat
EndEnumeration
Define.f MouseX,MouseY
ExamineDesktops()
DesktopW=DesktopWidth(0)
DesktopH=DesktopHeight(0)
If LoadFont(#Font, "", 10,#PB_Font_Bold)
SetGadgetFont(#PB_Default, FontID(#Font))
EndIf
If LoadFont(#Font2, "", 14,#PB_Font_Bold)
SetGadgetFont(#PB_Default, FontID(#Font2))
EndIf
If OpenWindow(#MainWindow, 0, 0, DesktopW, DesktopH, "Relief Mapping")
;AntialiasingMode(#PB_AntialiasingMode_x6)
If OpenWindowedScreen(WindowID(#MainWindow), 0, 0, DesktopW, DesktopH, 0, 0, 0)
;####You'll Have To Comment CPUName() Out If Your Using A Version Previous To 5.2####
CPU$ = CPUName()
;####Set Path To Where You Saved The Textures###################################
Add3DArchive("C:\Users\Bill\Music\Pictures\3D\", #PB_3DArchive_FileSystem)
;################################################################################################
;####Set Path To Where You Saved The CG and Material Files#############################################
Add3DArchive("C:\Users\Bill\Documents\Purebasic\PurebasicScripts\Relief Mapping",#PB_3DArchive_FileSystem)
;################################################################################################
;####Checking Archives For Material Script####
Parse3DScripts()
;####Load The Relief Map Material####
GetScriptMaterial(#ReliefMat,"MaterialOne")
;####Create Sprites For Help and Information####
CreateSprite(#FPS, 350, 210)
StartDrawing(SpriteOutput(#FPS))
Box(0,0,400,120,RGB(40,40,40))
StopDrawing()
CreateSprite(#Instructions, 350, 260)
StartDrawing(SpriteOutput(#Instructions))
Box(0,0,350,260,RGB(40,40,40))
DrawingFont(FontID(#Font2))
DrawText(2,12,"Press H to Hide/Show Help.",RGB(255,255,255),RGB(40,40,40))
DrawText(2,42,"Press Arrow Keys To Rotate Entity.",RGB(255,255,255),RGB(40,40,40))
DrawText(2,72,"Press W To View WireFrame.",RGB(255,255,255),RGB(40,40,40))
DrawText(2,102,"Press E To View Texture.",RGB(255,255,255),RGB(40,40,40))
DrawText(2,132,"Press S To Turn Relief Mapping Off.",RGB(255,255,255),RGB(40,40,40))
DrawText(2,162,"Press D To Turn Relief Mapping On.",RGB(255,255,255),RGB(40,40,40))
DrawText(2,192,"Press X To Add More Depth.",RGB(255,255,255),RGB(40,40,40))
DrawText(2,222,"Press C To Subtract Depth.",RGB(255,255,255),RGB(40,40,40))
StopDrawing()
;####Load Plain Texture####
LoadTexture(#TextureMat,"DiffuseMap.png")
CreateMaterial(#TextureMat,TextureID(#TextureMat))
SetMaterialColor(#TextureMat, #PB_Material_SpecularColor, RGB(255,255,255))
MaterialShininess(#TextureMat,64)
;####Create Mesh And Entity####
CreateSphere(#Sphere,6,32,32)
;CreateCube(#Sphere,12)
BuildMeshTangents(#Sphere)
CreateEntity(#Sphere, MeshID(#Sphere), MaterialID(#ReliefMat),0,0,0)
;####Create Camera And Lights####
CreateCamera(#Camera, 0,0,100,100)
MoveCamera(#Camera, 0, 0, 30)
CameraLookAt(#Camera, 0, 0, 0)
CameraBackColor(#Camera, RGB(250, 150, 50))
CreateLight(#Light, RGB(50,0,0), 0,5,10);,#PB_Light_Directional)
SetLightColor(#Light, #PB_Light_DiffuseColor, RGB(250,250,250))
SetLightColor(#Light, #PB_Light_SpecularColor, RGB(255,255,255))
LightLookAt(#Light, 0, 0, 0)
;####Ambient Lighting Does Work. Uncomment It If You Would Like To Give It A Try.####
;AmbientColor(RGB(100,100,100))
;####Set The Depth For the Sphere Entity. Leave The Last 3 Parameters At Zero.####
Depth.f= 0.04
EntityCustomParameter(#Sphere,0,#Depth,Depth,0,0,0)
Repeat
;####Examine Keyboard Input####
If ExamineKeyboard()
If KeyboardPushed(#PB_Key_Up)
RotateEntity(#Sphere,-0.3,0,0,#PB_Relative)
ElseIf KeyboardPushed(#PB_Key_Down)
RotateEntity(#Sphere,0.3,0,0,#PB_Relative)
EndIf
If KeyboardPushed(#PB_Key_Left)
RotateEntity(#Sphere,0,-0.3,0,#PB_Relative)
ElseIf KeyboardPushed(#PB_Key_Right)
RotateEntity(#Sphere,0,0.3,0,#PB_Relative)
EndIf
If KeyboardReleased(#PB_Key_W)
CameraRenderMode(#Camera, #PB_Camera_Wireframe)
ElseIf KeyboardReleased(#PB_Key_E)
CameraRenderMode(#Camera, #PB_Camera_Textured)
ElseIf KeyboardReleased(#PB_Key_S)
SetEntityMaterial(#Sphere, MaterialID(#TextureMat))
ElseIf KeyboardReleased(#PB_Key_D)
SetEntityMaterial(#Sphere, MaterialID(#ReliefMat))
ElseIf KeyboardReleased(#PB_Key_H)
ShowHelp=ShowHelp+1
If ShowHelp>1
ShowHelp=0
EndIf
EndIf
If KeyboardPushed(#PB_Key_X)
Depth=Depth+0.001
If Depth>0.125
Depth=0.125
EndIf
EntityCustomParameter(#Sphere,0,#Depth,Depth,0,0,0)
ElseIf KeyboardPushed(#PB_Key_C)
Depth=Depth-0.001
If Depth<0.01
Depth=0.01
EndIf
EntityCustomParameter(#Sphere,0,#Depth,Depth,0,0,0)
EndIf
EndIf
RenderWorld()
;####Now That The 3D Has Been Rendered. Display The Sprites.####
ShowFPS=1
If ShowFPS=1
CurrentFPS = Engine3DFrameRate(#PB_Engine3D_Current)
AverageFPS = Engine3DFrameRate(#PB_Engine3D_Average)
MaximumFPS = Engine3DFrameRate(#PB_Engine3D_Maximum)
MinimumFPS = Engine3DFrameRate(#PB_Engine3D_Minimum)
CountTris=CountRenderedTriangles()
StartDrawing(SpriteOutput(#FPS))
Box(0,0,280,120,RGB(40,40,40))
DrawingFont(FontID(#Font))
DrawText(2,2,CPU$,RGB(255,0,0),RGB(40,40,40))
DrawText(2,22,"Current FPS : "+Str(CurrentFPS),RGB(0,255,255),RGB(40,40,40))
DrawText(2,42,"Average FPS : "+Str(AverageFPS),RGB(0,255,255),RGB(40,40,40))
DrawText(2,62,"Maximum FPS : "+Str(MaximumFPS),RGB(0,255,255),RGB(40,40,40))
DrawText(2,82,"Minimum FPS : "+Str(MinimumFPS),RGB(0,255,255),RGB(40,40,40))
DrawText(2,102,"Rendered Triangles : "+Str(CountTris),RGB(0,255,0),RGB(40,40,40))
StopDrawing()
DisplayTransparentSprite(#FPS,20,20)
If ShowHelp=0
DisplayTransparentSprite(#Instructions,20,150)
EndIf
If FirstFrame=0
Engine3DFrameRate(#PB_Engine3D_Reset)
FirstFrame=1
EndIf
EndIf
FlipBuffers()
Until WindowEvent() = #PB_Event_CloseWindow Or KeyboardPushed(#PB_Key_Escape)
EndIf
EndIf
End
So, if I made a mistake in setting this up. My excuse is the lack of sleep.
If you do have problems running this. Let me know and I'll see what I can do for ya. After I get my 4 hours of sleep that is. Then I'll be wide awake.




