Статьи
О Vertex Shader'ах (VS) для Visual Basic 6
VS - это программы, выполняемые видео-адаптером при прорисовке каждой вершиы в примитиве. Т.е., например, в треугольнике она выполнится 3 раза. С помощью этих программ можно манипулировать положением, цветом вершины, а так же координатами наложенных на примитив текстур. Появилась эта возможнасть, как и pixel shader в DirectX 8. VS состоят из инструкций и регистров о которых смотрите ближе к концу :)
 Как создать Vertex Shader
Во-первых, при создании D3DDevice необходимо указывать D3DCREATE_SOFTWARE_VERTEXPROCESSING или D3DCREATE_MIXED_VERTEXPROCESSING в BehaviorFlags, т.к. в DX8 VS могут обрабатываться только програмно.
Во-вторых, перед прорисовкой примитива с использованием Vertex shader, нужно выполнять следующее:
D3DDevice.SetRenderState D3DRS_SOFTWAREVERTEXPROCESSING, True
Ну, и после прорисовки поменять True на False, если в цикле будут рисоваться примитивы без использования VS.
VS можно составлять прямо в программе, если он неочень длинный, или в любом текстовом фаиле. Любой VS начинается с главной инструкции, в которой определяется его версия.
vs.1.1
Затем идут инструкции, о которых см. далее.
Как и pixel shader, VS компилируется с помощью D3DX8 и записывается в массив Long.
Dim buffer As D3DXBuffer, ErrLog As String, bConsts As D3DXBuffer
Set buffer = D3DX.AssembleShaderFromFile(App.Path & "\vs.txt", 0, ErrLog, bConsts)
Dim f() As Long
ReDim f(buffer.GetBufferSize / 4 - 1)
DXCopyMemory f(0), ByVal buffer.GetBufferPointer, buffer.GetBufferSize
Следующим шагом необходимо сделать следующее:
Dim decl As D3DXDECLARATOR
D3DX.DeclaratorFromFVF FVF, decl
Смысл этой операции в том, чтобы заполнить структуру D3DXDECLARATOR тем типом вершин, с которым Вы будете работать(FVF), т.е. тем форматом, каким записаны вершины в VertexBuffer.
Ну и осталось все эти данные передать D3DDevice.
D3DDevice.CreateVertexShader decl.Value(0), f(0), dwVertexShader, D3DUSAGE_SOFTWAREPROCESSING
dwVertexShader - это и будет то значение, с помощью которого вызывается VS при прорисовке.
D3DDevice.SetVertexShader dwVertexShader
После этого любая вершина будет рисоваться с учЈтом VS.
 Инструкции
Полный список инструкций Vertex Shader 1.1 :
add - Складывает 2 вектора
def - Объявляет константу
dp3 - 3-x компонентный DotProduct двух векторов
dp4 - 4-x компонентный DotProduct двух векторов
dst - Считает дистанцию между двумя векторами
exp - 2x с полной точностью
expp - 2x с частичной точностью
frc - Возвращает дробную часть каждого входного компонента
lit - Частичная поддержка освящения
log - log2(x) с полной точностью
logp - log2(x) с частичной точностью
m3x2 - 3х2 матричное умножение
m3x3 - 3х3 матричное умножение
m3x4 - 3х4 матричное умножение
m4x3 - 4х3 матричное умножение
m4x4 - 4х4 матричное умножение
mad - Перемножает 2 вектора и прибавляет 3й
max - Считает максимум двух векторов
min - Считает минимум двух векторов
mov - Копирует один вектор в другой
mul - Перемножает вектора
nop - Ничего не делает
rcp - Обратная величина(1/X)
rsq - 1/sqr(x)
sge - Сравнивает 2 векторв друг с другом.
slt - Сравнивает 2 вектора. slt-противоположная sge инструкция
sub - Вычитание
 Описание инструкций
add
Складывает два исходных вектора
add dest, src0, src1
Эквивалентно:
dest.x = src0.x + src1.x
dest.y = src0.y + src1.y
dest.z = src0.z + src1.z
dest.w = src0.w + src1.w
def
Объявляет константу
def dst, value
Записывает 4D вектор value в регистр dest. Например:
dst c0, 1.0, 1.0, 0.5, 0.11
dp3
3х компонентный DotProduct двух векторов
dp3 dest, src0, src1
Эквивалентно:
dest.w = (src0.x * src1.x) + (src0.y * src1.y) + (src0.z * src1.z)
dest.x = dest.y = dest.z = dest.w
dp4
4х компонентный DotProduct двух векторов
dp4 dest, src0, src1
Эквивалентно:
dest.w = (src0.x * src1.x) + (src0.y * src1.y) + (src0.z * src1.z) + (src0.w * src1.w)
'dest.x = dest.y = dest.z = не_используется
dst
Дистанция между двумя векторами
dst dest, src0, src1
Эквивалентно:
dest.x = 1
dest.y = src0.y * src1.y
dest.z = src0.z
dest.w = src1.w

exp
2x с полной точностью(21 бит)
exp dest, src
Эквивалентно:
dest.x = dest.y = dest.z = dest.w = 2 ^ src.w
expp
2x с неполной точностью(10 бит)
expp dest, src
Эквивалентно:
dim w, v
w = src.w
v = src.w - int(src.w)
dest.x = 2 ^ v
dest.y = w - v
dim tmp, tmpd
tmp = 2 ^ w
tmpd = tmp and &hffffff00
dest.z = tmpd
dest.w = 1
frc
Возвращает дробную часть каждого входного компонента
frc dest, src
Эквивалентно:
dest.x = src.x - int(src.x)
dest.y = src.y - int(src.y)
dest.z = src.z - int(src.z)
dest.w = src.w - int(src.w)
lit
Подсчитывает освещенность путЈм подсчЈта коэффициента из двух точек и экспоненты
lit dest, src
Эквивалентно:
dest.x = 1
dest.y = 0
dest.z = 0
dest.w = 1
dim power: power = src.w
const MAXPOWER = 127.9961
if power < -MAXPOWER then
power = -MAXPOWER
elseif power > MAXPOWER then
power = -MAXPOWER
end if
if src.x > 0 then
dest.y = src.x
if src.y > 0 then
dest.z = src.y ^ power
end if
end if

log
log2(x) с полной точностью(21 бит)
log dst, src
Эквивалентно:
dim v: v = abs(src)
if v <> 0 then
dest.x = dest.y = dest.z = dest.w = log(v)/log(2)
else
dest.x = dest.y = dest.z = dest.w = -FLT_MAX;
end if
logp
Эквивалентно log, но с 10-битной точностью.
m3x2
3х2 матричное умножение.
m3x2 dest, src0, src1
Эквивалентно на VB:
dest.x = (src0.x * src1.x) + (src0.x * src1.y) + (src0.x * src1.z)
dest.y = (src0.y * src1.x) + (src0.y * src1.y) + (src0.y * src1.z)
Или на языке инструкций:
dp3 r0.x, r1, c0
dp3 r0.y, r1, c1
Во всех матричных инструкциях src1 - должен быть равен первой константе d3dmatrix, переданной программой. Например, регистры с0, c1, c2, c3 будут равны значениям матрицы, переданной методом D3DDevice.SetVertexShaderConstant 0, mat, 4
m3x3
3х3 матричное умножение.
m3x3 dest, src0, src1
Эквивалентно на VB:
dest.x = (src0.x * src1.x) + (src0.y * src1.y) + (src0.z * src1.z)
dest.y = (src0.x * src2.x) + (src0.y * src2.y) + (src0.z * src2.z)
dest.z = (src0.x * src3.x) + (src0.y * src3.y) + (src0.z * src3.z)

Или на языке инструкций:
dp3 r0.x, r1, c0
dp3 r0.y, r1, c1
dp3 r0.z, r1, c2

m3x4
3х4 матричное умножение.
m3x4 dest, src0, src1
Эквивалентно на VB:
dest.x = (src0.x * src1.x) + (src0.x * src1.y) + (src0.x * src1.z)
dest.y = (src0.y * src1.x) + (src0.y * src1.y) + (src0.y * src1.z)
dest.z = (src0.z * src1.x) + (src0.z * src1.y) + (src0.z * src1.z)
dest.w = (src0.w * src1.x) + (src0.w * src1.y) + (src0.w * src1.z)
Или на языке инструкций:
dp3 r0.x, r1, c0
dp3 r0.y, r1, c1
dp3 r0.z, r1, c2
dp3 r0.w, r1, c3
m4x3
4х3 матричное умножение.
m4x3 dest, src0, src1
Эквивалентно на VB:
dest.x = (src0.x * src1.x) + (src0.y * src1.y) + (src0.z * src1.z) + (src0.w * src1.w)
dest.y = (src0.x * src1.x) + (src0.y * src1.y) + (src0.z * src1.z) + (src0.w * src1.w)
dest.z = (src0.x * src1.x) + (src0.y * src1.y) + (src0.z * src1.z) + (src0.w * src1.w)
Или на языке инструкций:
dp4 r0.x, r1, c0
dp4 r0.y, r1, c1
dp4 r0.z, r1, c2

m4x4
4х4 матричное умножение.
m4x4 dest, src0, src1
Эквивалентно на VB:
dest.x = (src0.x * src1.x) + (src0.x * src1.y) + (src0.x * src1.z) + (src0.x * src1.w)
dest.y = (src0.y * src1.x) + (src0.y * src1.y) + (src0.y * src1.z) + (src0.y * src1.w)
dest.z = (src0.z * src1.x) + (src0.z * src1.y) + (src0.z * src1.z) + (src0.z * src1.w)
dest.w = (src0.w * src1.x) + (src0.w * src1.y) + (src0.z * src1.z) + (src0.w * src1.w)
Или на языке инструкций:
dp4 r0.x, r1, c0
dp4 r0.y, r1, c1
dp4 r0.z, r1, c2
dp4 r0.w, r1, c3

mad
Перемножает и складывает регистры
mad dest, src0, src1, src2
Эквивалентно:
dest.x = src0.x * src1.x + src2.x
dest.y = src0.y * src1.y + src2.y
dest.z = src0.z * src1.z + src2.z
dest.w = src0.w * src1.w + src2.w
max
Выбирает максимум из двух регистров
max dest, src0, src1
Эквивалентно:
dest.x=iif(src0.x >= src1.x, src0.x , src1.x)
dest.y=iif(src0.y >= src1.y, src0.y , src1.y)
dest.z=iif(src0.z >= src1.z, src0.z , src1.z)
dest.w=iif(src0.w >= src1.w, src0.w , src1.w)
min
Выбирает минимум из двух регистров
min dest, src0, src1
Эквивалентно:
dest.x=iif(src0.x < src1.x, src0.x , src1.x)
dest.y=iif(src0.y < src1.y, src0.y , src1.y)
dest.z=iif(src0.z < src1.z, src0.z , src1.z)
dest.w=iif(src0.w < src1.w, src0.w , src1.w)
mov
Переписывает один регистр в другой
mov dest, src
Эквивалентно:
dest.x=src.x
dest.y=src.y
dest.z=src.z
dest.w=src.w
mul
Перемножает регистры
mul dest, src0, src1
Эквивалентно:
dest.x = src0.x * src1.x
dest.y = src0.y * src1.y
dest.z = src0.z * src1.z
dest.w = src0.w * src1.w
rcp
1/X
rcp dest, src
Эквивалентно:
dim f: f = src0
if f = 0 then
f = FLT_MAX
else
if f <> 1.0 then
f = 1/f
end if
end if
dest = f
rsq
1/sqr(x)
rsq dest, src
Эквивалентно:
dim f: f = abs(src0)
if f = 0 then
f = FLT_MAX
else
if f <> 1 then f = 1/sqr(f)
end if
dest.z = dest.y = dest.z = dest.w = f

sge
Сравнение "больше или равно"
sge dest, src0, src1
Эквивалентно:
dest.x = iif(src0.x >= src1.x, 1, 0)
dest.y = iif(src0.y >= src1.y, 1, 0)
dest.z = iif(src0.z >= src1.z, 1, 0)
dest.w = iif(src0.w >= src1.w, 1, 0)

slt
Сравнение "меньше"
slt dest, src0, src1
Эквивалентно:
dest.x = iif(src0.x >= src1.x, 1, 0)
dest.y = iif(src0.y < src1.y, 1, 0)
dest.z = iif(src0.z < src1.z, 1, 0)
dest.w = iif(src0.w < src1.w, 1, 0)

sub
Вычитание векторов
sub dest, scr0, src1
Эквивалентно:
dest = src0 - src1
 Регистры
Входные регистры

 Регистр |
 Тип данных |
 Кол-во |
 Назначение |
 c# |
 4D вектор, single |
 96 |
 Константа |
 r# |
 4D вектор, single |
 12 |
 временный регистр |
 v# |
 4D вектор |
 16 |
 данные из VertexBuffer |
Выходные регистры

 Регистр |
 Тип |
 Кол-во |
 Назначение |
 oD# |
 4D vector |
 2 |
 oD0 \ oD1 - Diffuse \ Specular цвет вершины |
 oFog |
 скалярная величина |
 1 |
 Коэффициент тумана. Только oFog.x используется |
 oPos |
 4D vector |
 1 |
 Положение вершины после всех преобразований. Все компоненты (x, y, z, w) должны быть записаны. |
 oPts |
 скалярная |
 1 |
 Размер точки, если используется в качестве PointSprites |
 oT# |
 4D vector |
 8 |
 Координаты наложенных текстур |
 Модификаторы
1. _sat - модификатор инструкции. Записывает результат в промежутке от 0 до 1. Например: add_sat dest, src0, src1Не может быть использован для матричных инструкций(m3x2, ...)
2. " - " - модификатор регистра. Меняет знак регистра на противоположный. Например: add dest, src0, -src1
Для тех, кто всЈ понял небольшой примерчик ;)
 Небольшое примечание
Когда начнЈте творить, увидите, что при прорисовке, вне зависимости от положения камеры, у Ваших вершин не будет сохраняться пространственное положение... Т.е. все будет рисоваться на перпендикулярной зрителю плоскости... Как с этим бороться? Вот так:
Получите матрицу вида и проекции и перемножте их(а если меняется и матрица мира(например вращается), то и ее так-же залейте в mat):
Dim matProj As D3DMATRIX
D3DDevice.GetTransform D3DTS_PROJECTION, matProj
D3DDevice.SetTransform D3DTS_VIEW, matView
Dim mat As D3DMATRIX
D3DXMatrixIdentity mat
D3DXMatrixMultiply mat, matView, matProj
D3DXMatrixTranspose mat, mat

Затем передайте получившуюся матрицу shader'у:
D3DDevice.SetVertexShaderConstant 0, mat, 4
А в самом VS в конце добавьте следующее:
m4x4 oPos, r0, c0
где r0 - будет записан результат ваших преобразований.
Если есть вопросы, пишите, постараюсь ответить.
Добавлено: 30 июня 2005
Отзывы читалелей
Hexfire
Молодец, отличная статья!
Давно не работал с 3D, пора бы вспомнить...
ЕщЈ раз респект.
Phil
Какая-то страшная вещь... Насколько я знаю, полигоны прорисовываются по нес-ко тыс. в секунду. ЭТО Ж КАК ТОРМОЗИТ ЭТО ДЕЛО ЕСЛИ ДЛЯ КАЖДОЙ ВЕРШИНЫ ПО ПРОГАММЕ? Если не прав, токо скажите...
Добавить отзыв
Eсли
вы уже читали эту статью, не сочтите за труд, напишите пару строк о ваших
впечатлениях . Они будут опубликованы здесь для других читателей.
|