Elite Games - Свобода среди звезд!

Уроки для программистов - Спрайты на шейдерах

Глава 9. Спрайты на шейдерах

Как я и обещал – мы перепишем рендер спрайтов. Сейчас у нас он выглядит следующим образом:

procedure JLSprite.Render(WithChild:boolean);
var v1,v2,v3,v4:TDotVector3;
const
Tex1:TDotVector2=(x:0;y:0);
Tex2:TDotVector2=(x:1;y:0);
Tex3:TDotVector2=(x:1;y:1);
Tex4:TDotVector2=(x:0;y:1);
begin
v1:=dotVecAdd3(JLGetActiveCamera.V1,trans.pos);
v2:=dotVecAdd3(JLGetActiveCamera.V2,trans.pos);
v3:=dotVecAdd3(JLGetActiveCamera.V3,trans.pos);
v4:=dotVecAdd3(JLGetActiveCamera.V4,trans.pos);

if TexID<>'' then
textures.GetObjByName(TexID).Bind;

glColor3fv(@ color);


glBegin(GL_QUADS);
glTexCoord2fv(@ tex1);
glVertex3fv(@ v1);
glTexCoord2fv(@ tex2);
glVertex3fv(@ v2);
glTexCoord2fv(@ tex3);
glVertex3fv(@ v3);
glTexCoord2fv(@ tex4);
glVertex3fv(@ v4);
glEnd;
end;


Нам нужно убрать векторы v1, v2, v3, v4 отсюда, а значит и вообще (из класса камеры). Мы расширим рендер спрайтов, теперь они будут иметь параметр «вращение». Он нужен для того чтобы спрайты не выглядели одинаково. Итак, опишем псевдокодом, что нужно шейдеру.

-Позиция вершины (стандарт)
-Размер спрайта (передадим через нормаль)
-Матрица 2х2 поворота (передадим через специальную команду glProgramLocalParameter4fARB)

Объявим входящие и исходящие данные:

ATTRIB inPos = vertex.position;
ATTRIB inColor = vertex.color;
ATTRIB inSize = vertex.normal;
ATTRIB inTex = vertex.texcoord[0];

OUTPUT outPos = result.position;
OUTPUT outColor = result.color;
OUTPUT outTex = result.texcoord[0];

PARAM rotmat[2] = { program.local[0..1] };
PARAM mv[4] = { state.matrix.modelview };
PARAM pr[4] = { state.matrix.projection };

В mv пихаем modelview матрицу в pr projection. Почему не сразу mvp? Да потому что мы будем делать промежуточные расчеты.

TEMP vec,vecT;

Объявили темповые переменные.

inSize – это некий вектор в экранных координатах.

MOV vec.x,inSize.x;
MOV vec.y,inSize.y;
MOV vec.z,0;
MOV vec.w,1;

Запихиваем его в vec.


DP3 vecT.x, rotmat[0], vec;
DP3 vecT.y, rotmat[1], vec;
DP3 vecT.z, vec.zzzw, vec;

Умножаем его на матрицу поворота, которую мы передали в шейдер.

DP4 vec.x, mv[0], inPos;
DP4 vec.y, mv[1], inPos;
DP4 vec.z, mv[2], inPos;
DP4 vec.w, mv[3], inPos;

Переводим координаты вершины в modelview пространство.

ADD vec, vecT, vec;

Прибавляем этот вектор.

И теперь остается перевести координату вершины в пространство экрана с помощью матрицы проекции:

DP4 outPos.x, pr[0], vec;
DP4 outPos.y, pr[1], vec;
DP4 outPos.z, pr[2], vec;
DP4 outPos.w, pr[3], vec;

Вот полный код:

!!ARBvp1.0
ATTRIB inPos = vertex.position;
ATTRIB inColor = vertex.color;
ATTRIB inSize = vertex.normal;
ATTRIB inTex = vertex.texcoord[0];

OUTPUT outPos = result.position;
OUTPUT outColor = result.color;
OUTPUT outTex = result.texcoord[0];

PARAM mv[4] = { state.matrix.modelview };
PARAM pr[4] = { state.matrix.projection };
PARAM rotmat[2] = { program.local[0..1] };

TEMP vec,vecT;


MOV vec.x,inSize.x;
MOV vec.y,inSize.y;
MOV vec.z,0;
MOV vec.w,1;

DP3 vecT.x, rotmat[0], vec;
DP3 vecT.y, rotmat[1], vec;
DP3 vecT.z, vec.zzzw, vec;

DP4 vec.x, mv[0], inPos;
DP4 vec.y, mv[1], inPos;
DP4 vec.z, mv[2], inPos;
DP4 vec.w, mv[3], inPos;

ADD vec, vecT, vec;

DP4 outPos.x, pr[0], vec;
DP4 outPos.y, pr[1], vec;
DP4 outPos.z, pr[2], vec;
DP4 outPos.w, pr[3], vec;

MOV outTex, inTex;
MOV outColor, inColor;

END


Процедура рендера теперь другая:

sha:=shaders.GetShaderByName('billboard');
sha.Bind;

size:=size/10;
glProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, 0, pmat[0][0], pmat[0][1], 0, 1);
glProgramLocalParameter4fARB(GL_VERTEX_PROGRAM_ARB, 1, pmat[1][0], pmat[1][1], 0, 1);
glBegin(GL_QUADS);
glNormal3f(size,size,0);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 0);
glVertex3fv(@ pos);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 0);
glNormal3f(-size,size,0);
glVertex3fv(@ pos);
glNormal3f(-size,-size,0);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 1);
glVertex3fv(@ pos);
glNormal3f(size,-size,0);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 1);
glVertex3fv(@ pos);
glEnd;

sha.UnBind;


pmat-матрица 2х2 рассчитываемая следующим образом:

function jlMat2(ang:single):TDotMatrix2x2;
var fs,fc:Extended;
begin
Sincos(ang,fs,fc);

Result[0][0] := fc;
Result[0][1] := fs;

Result[1][0] := -fs;
Result[1][1] := fc;
end;

pmat:=jlMat2(jldegtorad(Random(359)));
Jurec
К началу раздела | Наверх страницы Сообщить об ошибке
Уроки для программистов - Спрайты на шейдерах
Все документы раздела: Для тех, кто хочет писать игры | Движок на OpenGL | Создание игр в Game Maker | Bump mapping | Использование Direct Input | XNA framework |


Дизайн Elite Games V5 beta.18
EGM Elite Games Manager v5.17 02.05.2010