Popular Post Kernell Posted February 16, 2015 Popular Post Share Posted February 16, 2015 (edited) Как я и обещал – публикую статью о некоторых возможностях шейдера и MTA. Речь пойдёт об изменении цвета нужных нам компонентов автомобиля (а точнее текстуры, которые наложены на полигоны этих компонентов) с помощью пиксельного и вершинного шейдеров. В качестве примера, я буду использовать стандартный автомобиль Sultan, а перекрашивать я ему буду диски на колёсах. На самом деле данный метод будет работать на любых объектах игры, будь-то это объекты ландшафта или же персонажи. Но так как сегодня мы будем работать с ZModeler 2, то их рассматривать мы не будем (так как ZM2 не умеет работать с костями персонажей и плохо экспортирует обычные объекты). Сразу оговорюсь – имеется ввиду, что вы уже владеете основами работы с ZModeler 2, поэтому детально рассматривать эту программу мы не будем. К счастью русскоязычных уроков по ZM2 очень много. Подготовка И так, мы имеем проект с уже готовым автомобилем, который без проблем работает в нашей любимой игре. Теперь можно приступать к делу. Для того чтобы MTA смогла найти с каким материалом ей работать — мы должны передать ей имя с помощью engineApplyShaderToWorldTexture. При экспорте модели из ZModeler 2 все материалы принимают название первой текстуры в самом материале. Т.е. если ваша текстура называется WHEELS.tga – то материал будет называться WHEELS. Это очень важно, так как если вы просто покрасите колёса и не установите текстуру (хотя бы в 1px), то материал в игре будет называться unnamed, как и все у которых нет текстур. Реализация Так уж случилось, что диски моего автомобиля имеют текстуру vehiclegeneric256, которая не является уникальной (ею окрашены ещё другие части автомобиля) – это не редкое явление. Чтобы решить эту проблему, придётся создать копию этой текстуры и назвать так как нам нужно. Далее мы создаём новый материал, называем его как нам удобно (я предпочитаю чтобы имена материалов и имена текстур совпадали), добавляем новую текстуру, выделяем нужные нам полигоны и применяем к ним наш материал. Экспортируем нашу модель.. и а этом наша работа с ZModeler2 закончена. Пишем шейдер Сам по себе шейдер будет очень простой, мы создадим одну переменную которой мы сможем задавать значения цвета из скрипта, все остальные данные нам предоставит MTA. Для загрузки шейдера я использовал следующий скрипт (он очень простой, поэтому отдельно его рассматривать мы не будем): addEventHandler( "onClientResourceStart", resourceRoot, function() local shader = dxCreateShader( "color.fx" ); if shader then dxSetShaderValue( shader, "color", { 1.0, 0.5, 0.0 } ); engineApplyShaderToWorldTexture( shader, "wheels256" ); end end ); Теперь займёмся написанием самого шейдера. Объявляем переменные: float4 color = 1; sampler Sampler0 : register( s0 ); В качестве цвета мы используем переменную color - её значение мы будем устанавливать с помощью engineApplyShaderToWorldTexture Теперь нам необходимо взять текущий цвет пикселя и «смешать» его с нашим цветом (color): struct PSInput { float2 TexCoord : TEXCOORD0; }; float4 PixelShaderFunction( PSInput PS ) : COLOR0 { float4 texColor = tex2D( Sampler0, PS.TexCoord ); texColor *= color; return texColor; } Всё что и делает этот шейдер – просто перекрашивает пиксели текстуры. Как видно мы не учитываем освещение из-за чего наши колёса будут светиться в темноте как огни. И тут на помощь к нам приходит mta-helper.fx написанный нашими любимыми The MTA Team =) Так же они для нас написали замечательную функцию которая просчитывает дифьюзный цвет автомобиля с учётом глобального затенения мира – MTACalcGTAVehicleDiffuse. Функция первым аргументом требует нормали вершин. Так как пиксельный шейдер работает непосредственно с изображением, информацию о нормалях мы не имеем. Для этого нам придётся написать вершинный шейдер. Исправленный код шейдера: #include "mta-helper.fx" float4 color = 1; sampler Sampler0 : register( s0 ); struct VSInput { float4 Position : POSITION0; float3 Normal : NORMAL0; float4 Diffuse : COLOR0; float2 TexCoord : TEXCOORD0; }; struct PSInput { float4 Position : POSITION0; float2 TexCoord : TEXCOORD0; float4 Diffuse : COLOR0; }; PSInput VertexShaderFunction( VSInput VS ) { PSInput PS = (PSInput)0; float4 worldPosition = mul( VS.Position, gWorld ); float4 viewPosition = mul( worldPosition, gView ); float4 position = mul( viewPosition, gProjection ); PS.Position = position; PS.TexCoord = VS.TexCoord; PS.Diffuse = MTACalcGTAVehicleDiffuse( VS.Normal, VS.Diffuse ); return PS; } float4 PixelShaderFunction( PSInput PS ) : COLOR0 { float4 texColor = tex2D( Sampler0, PS.TexCoord ); texColor *= PS.Diffuse * color; return texColor; } technique { pass P0 { VertexShader = compile vs_2_0 VertexShaderFunction(); PixelShader = compile ps_2_0 PixelShaderFunction(); } } В вершинном шейдере мы создаем новый объект типа PSInput, который будет подаваться на вход в нашу функцию PixelShaderFunction. Так как объект изначально не имеет никаких данных, наша задача передать их, как мы и сделали это с TexCoord, Diffuse и Position. Так как основной задачей вершинного шейдера является установка вершин, то он должен определить семантику POSITION для выходного параметра. В нашем случае это Position. Чтобы получить правильную позицию вершины для пиксельного шейдера, мы умножаем её на мировую матрицу объекта (gWorld), затем на матрицу вида камеры (gView), а затем и на матрицу проекции камеры (gProjection). Далее всё просто, с помощью функции написанной разработчиками MTA мы просчитываем новый цвет и передаём в Diffuse. В пиксельном шейдере мы просто умножаем полученный Diffuse на наш color, вот собственно вот и всё. Пишем скрипт для шейдера Скрипт который я использовал выше не подходит для работы на реальном сервере, так как не поддерживает работу с несколькими объектами, ибо шейдер глобальный, а значит цвет колёс у всех машин будет один. Для этого нам нужно для каждого автомобиля создавать новый объект шейдера, который будет иметь своё значение цвета. Хранить информацию о цвете колёс мы будем в ElementData в виде таблицы – это позволит устанавливать цвет с серверной стороны (для этого мы будем отслеживать событие onClientElementDataChange). Так же мы будем отслеживать события onClientElementStreamIn, onClientElementStreamOut и onClientElementDestroy. Событие onClientElementStreamIn необходимо нам для того чтобы создать шейдер когда автомобиль попадёт в стример клиента, остальные два – для удаления шейдера. Скрипт шейдера: local vehicles = {}; function CreateVehicleShader( vehicle ) local color = getElementData( vehicle, "WheelsColor" ); if color then local shader = dxCreateShader( "color.fx" ); if shader then vehicles[ vehicle ] = shader; dxSetShaderValue( shader, "color", color ); engineApplyShaderToWorldTexture( shader, "wheels256", vehicle ); return true; end end return false; end function CheckVehicleShader( element ) local vehicle = element or source; if vehicle and getElementType( vehicle ) == "vehicle" then if vehicles[ vehicle ] then return true; end return CreateVehicleShader( vehicle ); end return false; end function DestroyVehicleShader( element ) local vehicle = element or source; if vehicles[ vehicle ] then destroyElement( vehicles[ vehicle ] ); vehicles[ vehicle ] = NULL; end end addEventHandler( "onClientElementStreamIn", root, CheckVehicleShader ); addEventHandler( "onClientElementStreamOut", root, DestroyVehicleShader ); addEventHandler( "onClientElementDestroy", root, DestroyVehicleShader ); addEventHandler( "onClientElementDataChange", root, function( key, oldValue ) if key == "WheelsColor" and getElementType( source ) == "vehicle" and CheckVehicleShader( source ) then dxSetShaderValue( vehicle[ source ], "color", getElementData( source, "WheelsColor" ) ); end end ); addEventHandler( "onClientResourceStart", resourceRoot, function() for i, vehicle in ipairs( getElementsByType( "vehicle", root, true ) ) do CheckVehicleShader( vehicle ); end end ); Как можно было заметить, функция CreateVehicleShader создаёт шейдер для нашего автомобиля. В случае успеха возвращает true. Вызов функции происходит только из CheckVehicleShader - она проверяет наличие шейдера для автомобиля, если его нет - создаёт его. В случае успеха так же возвращает true. Так как при старте ресурса в стримере уже могут быть автомобили и они могут иметь информацию о цвете (WheelsColor), мы используем событие onClientResourceStart для проверки всех автомобилей, что находятся в стримере. onClientElementDataChange мы отслеживаем на случай изменения информации о цвете со стороны сервера. Так как изначально её может не быть, то шейдер не будет создан при старте ресурса или когда автомобиль попадёт в стример игрока - для этого у нас есть функция CheckVehicleShader. Edited February 18, 2015 by Guest 4 Link to comment
Scripting Moderators Sarrum Posted February 17, 2015 Scripting Moderators Share Posted February 17, 2015 Спасибо за статью. Link to comment
FranklinRoosevelt Posted February 17, 2015 Share Posted February 17, 2015 Воу, казалось бы проще всё это делается. Спасибо. И ещё, эту "процедуру" нужно делать для каждого авто, или для каждой модели дисков? Link to comment
Kernell Posted February 18, 2015 Author Share Posted February 18, 2015 И ещё, эту "процедуру" нужно делать для каждого авто, или для каждой модели дисков? Сразу видно, кто не читал статью Эта "процедура" выполняется конкретно для материала. Link to comment
FranklinRoosevelt Posted February 18, 2015 Share Posted February 18, 2015 Сразу видно, кто не читал статью Да я читал, но с телефона, не особо внимательно. Link to comment
FranklinRoosevelt Posted March 4, 2015 Share Posted March 4, 2015 Ещё вопрос, ZM 2, как было сказано, хорошо использовать для авто, а как на счет другие моделей (скины, обычные объекты), какую программу лучше использовать для них? И ещё, не могу понять где ты тут именно используешь саму текстуру, то над чем работал в ZM 2, как название текстуры в engineApplyShaderToWorldTexture? Писал тебе в личку, но только потом заметил твою подпись . Link to comment
Kernell Posted March 4, 2015 Author Share Posted March 4, 2015 Ещё вопрос, ZM 2, как было сказано, хорошо использовать для авто, а как на счет другие моделей (скины, обычные объекты), какую программу лучше использовать для них? 3ds Max + KAM's Script И ещё, не могу понять где ты тут именно используешь саму текстуру, то над чем работал в ZM 2, как название текстуры в engineApplyShaderToWorldTexture? Не совсем понимаю, что значит "используешь текстуру". Попытаюсь разобраться. Текстура WHEELS.tga - в моём случае это копия vehiclegeneric256 (как было сказано выше). Но вообще использовать можно любую текстуру, главное её потом наложить на нужные полигоны. Далее мы её запаковываем в TXD архив, и после чего следует обычная процедура замены моделей в MTA (engineReplaceDFF и т.д.). Писал тебе в личку, но только потом заметил твою подпись . Для того и существует форум - чтобы другие могли найти решение своих вопросов. Link to comment
FranklinRoosevelt Posted March 5, 2015 Share Posted March 5, 2015 Не совсем понял, давай разложим все по полочкам сначала ты берешь стандартную текстуру авто, в данном случае Султана(.dff), в ZM устанавливаешь специальный флаг (prim, sec или 3dn) на материал колес, далее что? Link to comment
Kernell Posted March 5, 2015 Author Share Posted March 5, 2015 Не совсем понял, давай разложим все по полочкам сначала ты берешь стандартную текстуру авто, в данном случае Султана(.dff), в ZM устанавливаешь специальный флаг (prim, sec или 3dn) на материал колес, далее что? Никаких флагов я не устанавливаю, они и не нужны. Я просто взял текстуру, скопировал и назвал по своему, далее загрузил в материал и применил материал к нужным полигонам. Link to comment
FranklinRoosevelt Posted March 5, 2015 Share Posted March 5, 2015 Ещё вопрос, ZM 2, как было сказано, хорошо использовать для авто, а как на счет другие моделей (скины, обычные объекты), какую программу лучше использовать для них? 3ds Max + KAM's Script KAM's_Scripts для экспорта текстур с gta.img и т.п.? UP: Все, понял что это плагин для 3ds Max'a, для dff моделей. Ступил что сразу не увидел. Link to comment
FranklinRoosevelt Posted March 14, 2015 Share Posted March 14, 2015 Kernell, привет это опять я. Я до сих пор ломаю голову над этой же задачей, посмотрел 100500 уроков, у меня назрела куча вопросов, в основном всё касательно работы над моделькой в 3ds max. Я начну пошагово: 1) Сначала мы через GTA Crazy IMG Editor достаем модельку (DFF) и текстуру (TXD), но вся работа будет делается же только над моделькой, можно сказать текстура здесь не нужна, так ведь? В нашем случае это sultan.dff, импортируем его в 3ds max через KAM's Script вот что у нас получилось: 2) Скрываем этот квадрат на колесе чтобы можно было выделить полигоны самого диска, вот: 3) Копируем этот диск что выделили (Clone to element): 4) Создаем новый материал (add), называем WHEELS(это то имя что нужно использовать в engineApplyShaderToWorldTextreu??) и sub-material просто копируем: 5) Экспортируем тоже через KAM's Script: и тут у меня вылезла какая-то ошибка: Получилась новая модель которую нужно просто заменить через EngineReplaceModel? Но мне кажется что это работать не будет, помоги разгрести эту кашу, скажи что я сделал правильно, а что нет, заранее огромное спасибо. Далее мы её запаковываем в TXD архив, и после чего следует обычная процедура замены моделей в MTA (engineReplaceDFF и т.д.). Стоп, запаковываем текстуру (TXD), а заменяем модель (DFF), я запутался. Надеюсь ты не поленишься и ответишь, ведь мне кажется у многих новичков и моделирование вроде меня с этим могут быть проблемы, а инфы очень мало. Link to comment
Kernell Posted March 15, 2015 Author Share Posted March 15, 2015 1) Тут где-то на форуме рекомендовали не пользоваться GTA Crazy IMG Editor (нужно искать), а использовать IMG Tool 2.0 2) Этот квадрат называется LOD (Level of Detail, где-то видел даже такой вариант - Low Object Distance). 3) Зачем клонировать диск? Достаточно просто к нему применить новый материал. 4) Я не знаю как поступает KAM`s Script, но: При экспорте модели из ZModeler 2 все материалы принимают название первой текстуры в самом материале. Т.е. если ваша текстура называется WHEELS.tga – то материал будет называться WHEELS. Это очень важно, так как если вы просто покрасите колёса и не установите текстуру (хотя бы в 1px), то материал в игре будет называться unnamed, как и все у которых нет текстур. 5) Судя по ошибке KAM's Script, ты криво его установил, так как он не может найти информацию о материалах. Надеюсь ты не поленишься и ответишь, ведь мне кажется у многих новичков и моделирование вроде меня с этим могут быть проблемы, а инфы очень мало. Эта тема не по моделированию, и даже не по 3D Max`у. Максимум на что я могу ответить в этой теме - по шейдерам, Lua, MTA и немного о ZModeler2. Далее мы её запаковываем в TXD архив, и после чего следует обычная процедура замены моделей в MTA (engineReplaceDFF и т.д.). Стоп, запаковываем текстуру (TXD), а заменяем модель (DFF), я запутался. Сказано же - обычная процедура. TXD и DFF в любом случае нужно загружать и заменять, так как мы имеем модифицированную модель и текстуры для неё. P.S. Только у меня Submit в Quick Reply не работает в этой теме? 1 Link to comment
FranklinRoosevelt Posted March 15, 2015 Share Posted March 15, 2015 Kernell, спасибо, теперь все понял. Link to comment
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now