Собственные шейдеры - Текстурирование - Каталог статей - Официальный сайт Hell494
Приветствую Вас 1-класс | RSS Главная | Каталог статей | Регистрация | Вход
Меню сайта

Категории раздела
Программирование [7]
Моделирование [8]
Маппинг [67]
Текстурирование [10]

Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0

Мини чат
200

Главная » Статьи » Текстурирование

Собственные шейдеры
Собственные шейдеры
Valve обещала дать возможность создавать свои собственные шейдеры под HL2. (Возможно Valve скоро выпустит этот инструментарий и будет доступен с очередным SourceSDK update)
Создавать шейдеры очень не просто, поэтому я решил опубликовать небольшой пример.
Я использовал программу RenderMonkey (Это версия работает только с установленным DirectX 9.0c) от aTI. Скачать можно по следующему адресу: http://www2.ati.com/developer/rendermonkey/files/RenderMonkey.2004-08-03-v1.5.424.exe (46 Мб).
Все наверняка помнят следующую тех демку от aTI:

Вооружившись презентациями по этой демке, я создал упрощённую модель. Упрощённую потому, что у меня всего дишь Radeon 8500, который может выполнить не более 22 aLU (пиксельный шейдер 1.4).
Пример 100% будет работать на следующих картах:

RaDEON™ X850 series RaDEON™ 9600 series
RaDEON™ X800 series RaDEON™ 9500 series
RaDEON™ X600 series RaDEON™ 9200 series
RaDEON™ X300 series RaDEON™ 9100 series
RaDEON™ 9800 series RaDEON™ 9000 series
RaDEON™ 9700 series RaDEON™ 8500 series

Я создал следующую структуру проекта:

NormalMap текстура для машины (я использовал эту текстуру в разрешении 2048х2048):

Теперь переёдем непосредственно к самим шейдерам:
Пиксельный шейдер:

sampler normalMap;
sampler showroomMap;
float4 paintColor0;
float4 paintColor2;
float4 paintColorMid;
float glossLevel;
float brightnessFactor;

float4 ps_main( //float4 Diff: COLOR0,
float2 Tex :TEXCOORD0,
float3 Tangent :TEXCOORD1,
float3 Binormal :TEXCOORD2,
float3 Normal :TEXCOORD3,
float3 View :TEXCOORD4 ) : COLOR
{
float3 vNormal = tex2D(normalMap,Tex);
vNormal = 2 * vNormal - 1.0;

float3x3 mTangentToWorld = transpose ( float3x3( Tangent, Binormal, Normal ));
float3 vNormalWorld = mul(mTangentToWorld,vNormal);
float4 fNdotV = mul( vNormalWorld, View );
float fNdotVSq = fNdotV * fNdotV;
float4 paintColor = fNdotV * paintColor0 +
fNdotVSq * paintColorMid +
fNdotVSq * fNdotVSq * paintColor2;

float fFresnel = saturate(dot( vNormalWorld, View));
float3 vReflection = 2 * vNormalWorld * fFresnel - View;

float fEnvBias = glossLevel;

// Sample environment map using this reflection vector and bias:
float4 envMap = texCUBE( showroomMap, float4( vReflection, fEnvBias ) );

// Premultiply by alpha:
envMap.rgb = envMap.rgb * envMap.a;

// Brighten the environment map sampling result:
envMap.rgb *= brightnessFactor;

// Combine result of environment map reflection with the paint
//float fEnvContribution = 1.0 - 0.5 * fFresnel;

return float4 (envMap.rgb + paintColor , 1.0);//* fEnvContribution
}

Вершинный шейдер:

float4x4 view_proj_matrix;
float4 view_position;
float4x4 inv_view_matrix;

struct VS_OUTPUT
{
float4 Pos: POSITION;
float2 Tex :TEXCOORD0;
float3 Tangent :TEXCOORD1;
float3 Binormal :TEXCOORD2;
float3 Normal :TEXCOORD3;
float3 View :TEXCOORD4;
};

VS_OUTPUT main( float4 Pos: POSITION,
float3 Normal: NORMaL,
float2 Tex :TEXCOORD,
float3 Tangent: TaNGENT,
float3 Binormal : BINORMaL
)
{
VS_OUTPUT Out = (VS_OUTPUT) 0;

Out.Pos = mul(view_proj_matrix, Pos);

Out.View = normalize(mul(inv_view_matrix, float4( 0, 0, 0, 1)) - Pos );

Out.Tex = Tex;
Out.Normal = Normal;
Out.Tangent = Tangent;
Out.Binormal = Binormal;
return Out;
}

В результате этого должно получиться следующее:

Различные цвета можно изменять. В результате этого мы можем получить различные цветовые схемы машины.

Часть 2.
Из презентаций от aTI мы увидим, что у этой машины есть также слой микрочастиц:



В Render Monkey мной был создан проект со следующей структурой:

Для тех, у кого видеоадаптеры не поддерживают выполнение шейдеров 2-ого поколения, то им необходимо воспользоваться Reference Rasterizer . В моём понимании - это модуль к DirectX , который позволяет обрабатывать шейдеры программно, причём всех моделей ( Shader Model 1. x , Shader Model 2, Shader Model 3).
Этот Reference Rasterizer есть в комплекте DirectX SDK. (самый последний dxsdk_oct2004.exe)
Для слоя микрочастиц я использовал следующую текстуру:

Теперь перейдём к самим шейдерам:
Пиксельный шейдер:

sampler normalMap;
sampler microflakeNMap;
float microflakePerturbationa;
float normalPerturbation;
float microflakePerturbation;
float4 paintColor0;
float4 paintColor2;
float4 paintColorMid;
float4 flakeLayerColor;

float4 ps_main(float4 Diff: COLOR0,
float2 Tex :TEXCOORD0,
float3 Tangent :TEXCOORD1,
float3 Binormal :TEXCOORD2,
float3 Normal :TEXCOORD3,
float3 View :TEXCOORD4,
float3 SparkleTex : TEXCOORD5) : COLOR
{// Пертурбированная нормаль из карты шумов
float3 vNormal = tex2D(normalMap, Tex );
vNormal = 2 * vNormal - 1.0;
// Подсчитываем нормали для обоих слоев микрочастиц
float3 vFlakesNormal = 2 * tex2D(microflakeNMap, SparkleTex ) - 1;
float3 vNp1 = microflakePerturbationa * vFlakesNormal +
normalPerturbation * vNormal;
float3 vNp2 = microflakePerturbation * (vFlakesNormal + vNormal);
float3 vView = View;
float3x3 mTangentToWorld = transpose( float3x3( Tangent, Binormal, Normal ));
// Подсчитываем скалярные продукты нормализированного вектора обозревателя с нормалями для обоих слоев микрочастиц
float3 vNp1World = mul( mTangentToWorld, vNp1);
float fFresnel1 = saturate( dot( vNp1World, vView));
float3 vNp2World = mul( mTangentToWorld, vNp2);
float fFresnel2 = saturate( dot( vNp2World, vView));
// Компонуем многотональный цвет прослойки микрочастиц
float fFresnel1Sq = fFresnel1 * fFresnel1;
float4 paintColor = fFresnel1 * flakeLayerColor + fFresnel1Sq * flakeLayerColor +
fFresnel1Sq * fFresnel1Sq * flakeLayerColor+
pow( fFresnel2, 16 ) * flakeLayerColor;
return float4 ( paintColor);
}

Вершинный шейдер :

float4x4 view_proj_matrix;
float4x4 inv_view_matrix;
float fFlakeTilingFactor;

struct VS_OUTPUT
{
float4 Pos: POSITION;
float2 Tex : TEXCOORD0;
float3 Tangent :TEXCOORD1;
float3 Binormal :TEXCOORD2;
float3 Normal :TEXCOORD3;
float3 View: TEXCOORD4;
float3 SparkleTex : TEXCOORD5;
};

VS_OUTPUT vs_main( float4 Pos: POSITION,
float3 Normal : NORMaL ,
float2 Tex : TEXCOORD0,
float3 Tangent: TaNGENT,
float3 Binormal:BINORMaL )
{
VS_OUTPUT Out = (VS_OUTPUT) 0;
Out.Pos = mul(view_proj_matrix, Pos);
Out.View = normalize(mul(inv_view_matrix,float4(0, 0, 0, 1))- Pos);
Out.Tex = Tex ;
Out.Normal = Normal ;
Out.Tangent = Tangent;
Out.Binormal = Binormal;
Out.SparkleTex = float4( Tex * fFlakeTilingFactor,0,1);
return Out;
}

В результате мы должны получить следующее:

При более близком рассмотрении увидим слой микрочастиц, состоящий из маленьких квадратиков:

Конечно, изображение можно сделать гораздо лучше. Здесь уже многое зависит от самой текстуры микрочастиц, которую вы используете.
Часть 3.
Вот пояснения от Valve относительно шейдеров в HL 2:

Valve принимала участие в GDC 2004, а в результате на сайте aTI появился файл D3DTutorial10_Half-Life2_Shading.pdf (который также можно скачать с сайта aTI ). А пример будет строиться по подобному дереву эффектов в этой документации. В помощь приходит проектный файл Illumination.rfx, входящий в состав RM 1.6 (в нём содержится весь нужный нам код для описания подобной структуры). Разумеется, проект был немного переделан, чтоб как можно больше соответствовал структуре, описанной в документации. Также убраны все лишние объекты.

Исходный код, приводящийся в документации, я не использовал ввиду того, что это займёт не мало времени на переделку в структуру RM .
Вот структура, которую мы попытаемся сделать (Взято из D3DTutorial10_Half-Life2_Shading.pdf):



А вот как это получилось описать у меня для примера, который мы рассматриваем:



В Render Monkey проект выглядит следующим образом:



Для тех, у кого видеоадаптеры не поддерживают выполнение шейдеров 2-ого поколения, то им необходимо воспользоваться Reference Rasterizer . В моём понимании - это модуль к DirectX , который позволяет обрабатывать шейдеры программно, причём всех моделей ( Shader Model 1. x , Shader Model 2, Shader Model 3).
Этот Reference Rasterizer есть в комплекте DirectX SDK.
Т.к. у меня всего лишь Radeon 8500 ( PS 1.4), то мне пришлось им воспользоваться. Reference Rasterizer занимает где-то 8 Мб и его возможно установить отдельно от DX 9 SDK – достаточно скопировать нужные библиотеки в system 32.
Под PS 1.4 сделать подобный пример невозможно (даже если будет несколько проходов( pass )). Вот, например,
bump = normalize ( bump * 2.0 f ); занимает 4 операции, а это 1/4 PS 1.4.

Теперь перейдём к самим шейдерам:
Вершинный шейдер:
float4x4 view_proj_matrix: register(c0);
float4 light_position: register(c8);
float4 eye_position: register(c9);
float4x4 view_matrix: register(c10);
float4x4 inv_view_matrix;
struct VS_INPUT_StrUCT
{
float4 position: POSITION;
float3 normal: NORMaL;
float2 texcoord0: TEXCOORD0;
float3 binormal: BINORMaL0;
float3 tangent: TaNGENT0;
};

struct VS_OUTPUT_StrUCT
{
float4 position: POSITION;
float2 bump_map: TEXCOORD0;
float3 light_vector: TEXCOORD1;
float3 half_angle: TEXCOORD2;
float3 basis1: TEXCOORD3;
float3 basis2: TEXCOORD4;
float3 basis3: TEXCOORD5;
};

//** main - Главная точка входа для вершинного шейдера
//** Returns: VS_OUTPUT_StrUCT
//**---------------------------------------------------------
VS_OUTPUT_StrUCT main( VS_INPUT_StrUCT vsInStruct )
{
VS_OUTPUT_StrUCT vsOutStruct; //** Declare the output struct

vsOutStruct.position = mul( view_proj_matrix, vsInStruct.position );
float3 position = mul( view_matrix, vsInStruct.position );

//**----------------------------------------------
//** Проход для вычисления координат текстур bump и base
//**----------------------------------------------
vsOutStruct.bump_map = vsInStruct.texcoord0;

//**--------------------------------------------
//** Подсчёт вектора света в пространстве,
//** и далее трансформация его в область текстуры.
//**--------------------------------------------
float3 temp_light_position = mul( inv_view_matrix, light_position );
float3 temp_light_vector = temp_light_position - vsInStruct.position;
vsOutStruct.light_vector.x = dot( temp_light_vector, vsInStruct.tangent );
vsOutStruct.light_vector.y = dot( temp_light_vector, vsInStruct.binormal );
vsOutStruct.light_vector.z = dot( temp_light_vector, vsInStruct.normal );

//**-------------------------------------------
//** Calculate the view vector in object space,
//** and then transform it into texture space.
//**-------------------------------------------
float3 temp_eye_position = mul( inv_view_matrix, eye_position );
float3 temp_view_vector = temp_eye_position - vsInStruct.position;
float3 temp_view_vector2;
temp_view_vector2.x = dot( temp_view_vector, vsInStruct.tangent );
temp_view_vector2.y = dot( temp_view_vector, vsInStruct.binormal );
temp_view_vector2.z = dot( temp_view_vector, vsInStruct.normal );

//**-------------------------
//** Calculate the half angle
//**-------------------------
vsOutStruct.half_angle = vsOutStruct.light_vector + temp_view_vector2;

//**-----------------------------------------
//** Construct the transpose of the tangent
//** space basis vectors, so we can transform
//** the reflection vector from texture space
//** into view space
//**-----------------------------------------
vsOutStruct.basis1.x = vsInStruct.tangent.x;
vsOutStruct.basis1.y = vsInStruct.binormal.x;
vsOutStruct.basis1.z = vsInStruct.normal.x;
vsOutStruct.basis2.x = vsInStruct.tangent.y;
vsOutStruct.basis2.y = vsInStruct.binormal.y;
vsOutStruct.basis2.z = vsInStruct.normal.y;
vsOutStruct.basis3.x = vsInStruct.tangent.z;
vsOutStruct.basis3.y = vsInStruct.binormal.z;
vsOutStruct.basis3.z = vsInStruct.normal.z;

return vsOutStruct; //** Return the resulting output struct
}
В результате у вершинного шейдера 34 операции.

Пиксельный шейдер:
float4 specular: register(c6);
float Ka: register(c7);
float Kd: register(c8);
float Ks: register(c9);
float specular_power: register(c10);
float bumpiness: register(c11);
float reflection_clarity: register(c12);
float reflectance: register(c13);
float4 ambient: register(c4);
float4 diffuse: register(c5);
float4x4 view_matrix: register(c0);
sampler base_map: register(s0);
sampler bump_map: register(s1);
sampler environment_map: register(s2);
struct PS_INPUT_StrUCT
{
float2 bump_map: TEXCOORD0;
float3 light_vector: TEXCOORD1;
float3 half_angle: TEXCOORD2;
float3 basis1: TEXCOORD3;
float3 basis2: TEXCOORD4;
float3 basis3: TEXCOORD5;
};

struct PS_OUTPUT_StrUCT
{
float4 color0: COLOR0;
};

//**---------------------------------------------------------
//** Function: main
//** Description: Declare the main entry point for the shader
//** Input: PS_INPUT_StrUCT, derived from the output of
//** the associated vertex shader
//** Returns: PS_OUTPUT_StrUCT
//**---------------------------------------------------------
PS_OUTPUT_StrUCT main( PS_INPUT_StrUCT psInStruct )
{
PS_OUTPUT_StrUCT psOutStruct;

//**----------------------------------------
//** Get the base and bump map texture coord
//**----------------------------------------
float4 bump_coord = { psInStruct.bump_map, 0.0f, reflection_clarity };

//**------------------------------------------------------
//** Retreive the base color and bump components from the
//** respective textures, based on the passed bump coords.
//**------------------------------------------------------
float3 base = tex2D( base_map, bump_coord );
float3 bump = tex2D( bump_map, bump_coord );

//**--------------------------------------------------
//** Includes MIP bias to help clairify the reflection
//**--------------------------------------------------
float3 reflection_bump = tex2Dbias( bump_map, bump_coord );

//**----------------------------------------------------
//** Normalize the passed vectors from the vertex shader
//**----------------------------------------------------
float3 normalized_light_vector = normalize( psInStruct.light_vector );
float3 normalized_half_angle = normalize( psInStruct.half_angle );
//**--------------------------------------------------------
//** "Smooth out" the bumps based on the bumpiness parameter.
//** This is simply a linear interpolation between a "flat"
//** normal and a "bumped" normal. Note that this "flat"
//** normal is based on the texture space coordinate basis.
//**--------------------------------------------------------
float3 smooth;
smooth.r = 0.5f ;
smooth.g = 0.5f ;
smooth.b = 1.0f ;
bump = lerp( smooth, bump, bumpiness );
bump = normalize( ( bump * 2.0f ) - 1.0f );
reflection_bump = lerp( smooth, reflection_bump, bumpiness );
reflection_bump = normalize( ( reflection_bump * 2.0f ) - 1.0f );

//**---------------------------------------------
//** translate the reflection normal from texture
//** space into view space, so we can case the
//** reflection vector into an environment map.
//**---------------------------------------------
float3 reflection = reflection_bump;
reflection.x = dot( reflection_bump, psInStruct.basis1 );
reflection.y = dot( reflection_bump, psInStruct.basis2 );
reflection.z = dot( reflection_bump, psInStruct.basis3 );
float3 reflection_vector = mul( view_matrix, reflection );
reflection_vector = normalize( reflection_vector );

//**------------------------------------------
//** Calculate the resulting reflection color.
//**------------------------------------------
// float3 reflection_color = texCUBE( environment_map, reflection_vector );
float3 reflection_color = texCUBE( environment_map, reflection_vector );
//**----------------------------------------------------------
//** The following modifiers are used to enhance the effect of
//** the basic reflection idea. Normally, specular / gloss
//** maps will take the place of these modifiers.
//**----------------------------------------------------------
float3 result_color = lerp( base, reflection_color, reflectance );
// float3 modified_specular_color = specular;// * base;
// float3 modified_specular_coefficient = Ks;// * base;

//**---------------------------------------------------------
//** These dot products are used for the lighting model
//** equations. The surface normal dotted with the light
//** vector is denoted by n_dot_l. The normal vector
//** dotted with the half angle vector is denoted by n_dot_h.
//**---------------------------------------------------------
float3 n_dot_l = dot( bump, normalized_light_vector );
float3 n_dot_h = dot( bump, normalized_half_angle );
//**--------------------------------------
//** Далее идёт формирование финального результата
//** --------------------------------------
float3 Specular_final = ( specular * Ks * pow( max( 0, n_dot_h ), specular_power ) );
float3 Diffuse_final = ( diffuse * Kd * max( 0, n_dot_l ) );
float3 ambient_final = ( base * ambient*Ka);
float3 bump_Cube_specular = result_color * Specular_final;
float3 Diffuse_bump_ambient = Diffuse_final + ambient_final;
psOutStruct.color0.rgb =bump_Cube_specular+Diffuse_bump_ambient ;

//**------------------------------------
//** Interpolate the resulting color
//** based on the reflectance parameter.
//**------------------------------------
psOutStruct.color0.a = 1.0f ; //** Set the alpha component manually

return psOutStruct; //** Return the resulting output struct
}
У пиксельного шейдера 5 5 операций .
В результате мы должны получить следующее:

Можно менять значения объектов (например, specular), при этом мы будем получать разные результаты:

В результате мы получили технологию, похожую в Unreal 3 Engine

Рекомендую всем ознакомиться со статьёй по созданию шейдеров на gamedev . ru :
http://www.gamedev.ru/articles/read.shtml?id=10109&page=1
Автор описывает основы программирования на HLSL .

Спасибо всем за внимание.

Источник: http://source-inside.ru/index.php?id=141
Категория: Текстурирование | Добавил: HellMapper (02.10.2009) | Автор: DomenER
Просмотров: 373 | Рейтинг: 0.0/0 |
Всего комментариев: 0
Имя *:
Email *:
Код *:
Форма входа

Группа Steam

Наша кнопка

Поиск

Друзья сайта


  • Официальный блог


  • YIIbIpu-TEAM


  • DETLER-TEAM



  • Copyright WEDGe © 2016