x
> Хотя ты сделай нормальный порог, не 00, а там 0x00000020 какие-нибудь. Чтобы точно убедиться.
Убедился. Увы, так и есть: при пороге 0x00000060 блик выводится целиком, а при пороге 0x00000070 целиком не выводится. Получается, что альфа блика где-то между и т.к. она одинаковая во всех точках спрайта, то альфа тест рубит его либо целиком, либо вообще не рубит.
Я почитал тут: http://msdn.microsoft.com/en-us/library/windows/desktop/bb172254%… vs.85%29.aspx
Альфа-тест это, типа, штука для того, чтобы сэкономить на блендинге пикселя, поэтому он проводится раньше него.
x
Жжошь. То есть мои рассчеты с пониманием где именно работает не так (который кстати решил проблему для тех мест, где они проводились) - это метод тыка.
А вот это:
> Хотя ты сделай нормальный порог, не 00, а там 0x00000020 какие-нибудь. Чтобы точно убедиться.
Это не метод тыка, да?
Kozinaka
В общем подумал что нам надо более внимательно, и пришел к выводу что нам это не сделать на блендинге. Для внутренней части нам нужен INVSRCALPHA на dest, а для внешней - ONE.
1. Так что увы, остается мультитекстурирование (напомни почему ты не хочешь его использовать)
2. Рендер в стенсил буфер с альфатестом твоей маски, чтобы потом по ней резать
MrShoor
> Это не метод тыка, да?
Как будто метод тыка, это что-то негодяйское. Иногда бывает совсем не лишним ткнуть для верности. :)
> В общем подумал что нам надо более внимательно, и пришел к выводу что нам это не сделать на блендинге.
На самом деле для бликов может подойти и аддитивное наложение. Это же, по сути, отражение на стекле окружения и, больше всего, источников света с потолка, поэтому лёгкое свечение может быть вполне уместно. А общего уровня, равномерно высветляющего подложку на спрайте не будет. На моих картинках он тестовый. Будет полностью прозрачный спрайт с редкими светлыми полосами. Так чтобы они относительно неожиданно появлялись и проползали по стелку при движении камеры.
Итого, количество методов для вывода по альфа-маске более чем достаточно:
1. Мультитекстурирование.
2. Аддитивное наложение при получившимся в процессе обсуждения подходе. Остаётся прозрачность на накладываемом спрайте, но наложение аддитивное.
3. Наложение без прозрачности, если тупо использовать альфу из DEST. Это у меня случай извращенский - мне "стекло" на "стекло" надо накладывать.
В большинстве случаев сработает метод отсюда: http://stackoverflow.com/questions/5097145/opengl-mask-with-multiple-textures
4. Пиксельный шейдер. Возможно, если использовать ещё и вертексный шейдер, то всю логику наложения бликов можно в одном батче реализовать внутри шейдеров. Я в этом пока не силён, да и сложно в этом случае разделить графический движок от отрисовщика сцены, поэтому оставляю для себя это последним вариантом.
5. Рендер в стенсил буфер с альфатестом маски. Но обрезание попиксельное, поэтому в целом решение грубоватое.
>Напомни, почему ты не хочешь его использовать
Просто до этого обходился без мультитекстурирования, которое, насколько я понимаю, не всеми видеокартами поддерживается.
Плюс немного смущает, что мне нужно расширить формат вершины двумя лишними float'ами, которые реально нужны в 5% случаев отрисовки эффектов.
Батчить с мультитекстурированием вполне можно, единственное, что для каждого спрайта мне нужна обязательно маска и обязательно вырезанная из единого спрайта масок, так, чтобы ни спрайт бликов, ни спрайт масок не переключать, а только текстурными координатами рулить. В случае многопроходного наложения я на цельных фрагментах стекла могу вообще маски не рисовать, и накладывать маски и блики раздельно, слоями.
В целом, недостатки мультитекстурирования в моём случае не такие уж и серьёзные. Поэтому если не подойдёт аддитивное наложение бликов, то заряжу всё через мультитекстурирование.
Я не знаю, это еще нужно постараться, чтобы найти карточку, не поддерживающую мультитекстурирование. Это карточки произведенные в конце 90-х примерно. :)
А вообще я бы конечно на шейдеры переписал. Во первых FFP сейчас все равно эмулируется шейдерами. Во вторых - у тебя бы появилась шикарная гибкость в способах отрисовки. Тебе не придется жонглировать стейтами, как приходится это делать сейчас. Заморачиваться с какими-то премульт альфами, и прочей магией.
Я когда первый раз изучал шейдеры - тоже их не понимал и боялся. Сейчас пишу код только на шейдерах, а FFP использую максимум только для рендера дебагинфы.
MrShoor
> Сейчас пишу код только на шейдерах, а FFP использую максимум только для рендера дебагинфы.
В целом согласен, если железо хоть сколько-нибудь современное, то там будет и мультитекстурирование и шейдеры, а со вторыми работать гораздно гибче (если только тему прорубить). На старом железе всё равно придётся убирать эффекты, для которых все эти балалайки требуются.
Попробую в вершинном шейдере рассчитывать отрисовываемый фрагмент из спрайта бликов в зависимости от положения на экране.
MrShoor
>То есть мои рассчеты с пониманием где именно работает не так
Там не было понимания. Замена обычного блендинга на аддитивный это не есть понимание.
>Это не метод тыка, да?
Вряд ли. У автора в альфе черный цвет не был черным, во всяком случае на приведенном скриншоте.
x
> Там не было понимания. Замена обычного блендинга на аддитивный это не есть
> понимание.
То есть у автора фон вокруг затемнялся, я провел рассчеты и наобум угадал как сделать так, чтобы окружение не затемнялось? И причем тут аддитивный и понимание? Аддитивный блендинг - это блендинг, понимание - это понимание. Что за чушь ты несешь? Я с таким же успехом могу сказать, что ник x - это не есть наличие интеллекта. :) Вот и ищи логику.
MrShoor
Ясно.
MrShoor, x, не ругайтесь, пожалуйста.
Помогите, если не сложно, с шейдерами, а то застрял, как на грех, на самом начале.
Мой вершинный шейдер просто игнорируется и чтобы я из него не передавал, в пиксельный ничего не приходит.
Вот он:
struct VertexInput { float4 Position : POSITION; float2 TexCoord : TEXCOORD0; }; struct VertexOutput { float4 Position : POSITION; float2 TexCoord : TEXCOORD0; float2 Shift : TEXCOORD1; }; VertexOutput main(VertexInput input) { VertexOutput output; output.Position = input.Position; output.TexCoord = input.TexCoord; //output.TexCoord.x = 0.3; //output.TexCoord.y = 0.3; output.Shift.x = 0.5; output.Shift.y = 0.5; return output; }
А вот пиксельный:
sampler BlinkTexture : register(s0); sampler MaskTexture : register( s1); float4 main( float2 texCoord : TEXCOORD0, float2 shift : TEXCOORD1) : COLOR0 { //Сдвиг текстурных координат блика float2 blinkTex = texCoord; blinkTex.x = blinkTex.x + shift.x; blinkTex.y = blinkTex.y + shift.y; float4 blink = tex2D( BlinkTexture, blinkTex); float4 mask = tex2D( MaskTexture, texCoord * 2 /* блик в два раза растянут */); return float4( blink.r, blink.g, blink.b, blink.a * mask.a); }
В параметре shift - (0, 0) чем его не заряжай, причём даже если в вершинном шейдере output.TexCoord.x и y в константы выставлять то это ни на что не влияет.
Устанавливается вершинный шейдер успешно, но такое ощущение, что работает всё равно дефолтовый.
_d3dDevice->SetVertexShader(_blinkVertexShader) -> S_OK.
А с пиксельным всё хорошо, он работает как надо. Вот только ничего не получает дополнительного.
У меня в коде шейдеров ошибка или я где-то в их подключении туплю?
P.S. А формат вершины в вершинном буфере у меня такой:
struct Vertex { float x; float y; float z; float rhw; D3DCOLOR colour; float u; float v; };
Может иметь значение, что я его не повторяю точно во входных данных вершинного шейдера?
Имеет значение как ты заполнил декларации. Гугли D3DVERTEXELEMENT9
Скажи пожалуйста, а я могу не используя _d3dDevice->SetVertexDeclaration просто подогнать входные параметры вершинного шейдера под формат моих вершин в вершинном буфере (в моём предыдущем посте) выставленного с помощью _d3dDevice->SetFVF?
Сейчас я вот так пытаюсь подключать свой вершинный шейдер:
D3DVERTEXELEMENT9 vertexElement[] = { {0, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, {0, 16, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0}, D3DDECL_END() }; IDirect3DVertexDeclaration9* _decl = 0; HRESULT res = _d3dDevice->CreateVertexDeclaration(vertexElement, &_decl); _d3dDevice->SetVertexDeclaration(_decl); _d3dDevice->SetVertexShader(_blinkVertexShader);
Эта тема и сама, по ходу, не работает и рушит весь FFP вывод с прозрачностью (хотя я там убираю шейдеры и вызываю обратно SetFVF). :(
Кстати, если всё-таки использовать CreateVertexDeclaration, то вершинный буфер тоже нужно соответствующий для шейдеров создавать?
SetFVF плох тем привязывает тебя к четкому порядку данных в вершине, а так же ограниченным кол-вом данных.
Вот тут описано, как автоматически мапится/анмапится SetFVF на VertexDeclaration.
Но я бы от SetFVF уходил, ибо он ограничивает в гибкости.
Суммируя:
Для твоего формата вершин:
struct Vertex { float x; float y; float z; float rhw; D3DCOLOR colour; float u; float v; };
константа в SetFVF должна быть обязательно такой: D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1
Но в шейдере ты хочешь оперировать еще одной текстурной координатой. Соответственно твоя вершина уже не подходит, и нужно менять её на:
struct Vertex { float x; float y; float z; float rhw; D3DCOLOR colour; float u1; float v1; float v2; float u2; };
и константу для SetFVF менять на: D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1 | D3DFVF_TEX2
Тот D3DVERTEXELEMENT9 vertexElement[] работать не будет, потому что для вертекса:
struct Vertex { float x; float y; float z; float rhw; D3DCOLOR colour; float u; float v; };
Он должен быть такой:
D3DVERTEXELEMENT9 vertexElement[] = { {0, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, {0, 20, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0}, D3DDECL_END() };
потому что второй параметр - смещение от начала структуры, а утебя там D3DCOLOR colour;
Kozinaka
> Кстати, если всё-таки использовать CreateVertexDeclaration, то вершинный буфер
> тоже нужно соответствующий для шейдеров создавать?
Ну и по поводу этого вопроса. Суть VertexDeclaration - описать как у тебя данные располагаются внутри твоей структуры. Например у тебя есть стуктура:
struct Vertex { float x; float y; float z; float some_dummy1; float some_dummy2; float u1; float v1; float some_dummy3; float v2; float u2; };
в ней данные, которые ты хочешь в шейдере обработать, а именно координата, и две текстурные кооринаты.
Ты в VertexDeclaration указываешь смещение до данных, тип данных, семантику и индекс. Что такое смещение - я думаю понятно, это разница в указателях между адресом поля и адресом начала структуры.
Тип данных в виде D3DDECLTYPE_ я думаю тоже понятен. Чтобы DX мог понять размерность вектора в структуре и как его преобразовать к float4.
А семантика + индекс - это такая штука, чтобы разобраться какой вектор куда "присвоить" в шейдере. В принципе достаточно просто индекса, и ты можешь тупо брать и использовать скажем TEXCOORD семантику абсолютно для всего. Название же семантики тут введено только для удобства понимания.
Для вышеприведнной вершины семантика будет какая-то такая:
D3DVERTEXELEMENT9 vertexElement[] = { {0, 0, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, {0, 20, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0}, {0, 32, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1} D3DDECL_END() };
Так что суть всего этого VertexDeclaration - описать что где лежит в уже существующих форматах вершины, и понятное дело - ничего создавать не надо. Это просто правила конвертирования вершины в твоем С++ коде в стурктуру, описанную в HLSL.
Спасибо большое за подробный ответ! Исчерпывающе.
Попробую использовать только VertexDeclaration.
Тема в архиве.