Извините, ничего не найдено.

Не расстраивайся! Лучше выпей чайку!
Регистрация
Справка
Календарь

Вернуться   www.boolean.name > Программирование игр для компьютеров > Unity > Эффекты/Шейдеры

Ответ
 
Опции темы
Старый 16.06.2012, 12:54   #1
pie
ПроЭктировщик
 
Аватар для pie
 
Регистрация: 04.11.2011
Сообщений: 176
Написано 19 полезных сообщений
(для 64 пользователей)
Декали в Unity3D

Данная статья является переводом следующих статей:
Decals in Unity3D, Part 1
Decals in Unity3D, Part 2

Часть 1.

В первой части мы используем самый простой способ создания декали: создание префаба в точке контакта. Первым делом мы должны создать составные части нашего префаба: примитив plane и материал. Затем нам нужна текстура с альфа-каналом для того чтобы использовать шейдеры семейства Transparent в Unity. Наконец мы должны применить текстуру с альфа-каналом к созданному материалу. Вот наш plane с использованием шейдера Transparent/Diffuse и нанесенной на него текстурой:


Теперь мы просто создаем новый префаб в окне Project и перетаскиваем созданный plane на него.
Создаем новый C# скрипт в окне Project и называем его как хотим. В этом примере имя файла Decal01.cs, поэтому имя класса должно быть Decal01:

using UnityEngine;
using System.Collections;
public class 
Decal01 MonoBehaviour
{
   
// Use this for initialization 
   
void Start (){}
 
   
// Update is called once per frame
   
void Update (){} 
   } 
Мы должны постоянно проверять, есть ли столкновение с другим объектом прежде чем создать декаль, так что давайте добавим raycasting на наш код в Update ():

void Update ()
{
   
RaycastHit hit;
 
   if (
Physics.Raycast(camera.ScreenPointToRay(Input.mousePosition), out hit))
   {
 
   }

Теперь нам нужно создать какое то событие при котором будут создаваться наши декали, это событие будет нажатие левой кнопки мыши:

void Update ()
{
   
RaycastHit hit;
 
   if (
Physics.Raycast(camera.ScreenPointToRay(Input.mousePosition), out hit))
   {
     if (
Input.GetMouseButtonDown(0))
     {
     }
   }

Создаем GameObject переменную которая будет содержать наш префаб с декалью.

using UnityEngine;
using System.Collections;
 
public class 
Decal01 MonoBehaviour
{
   public 
GameObject decalPrefab;
 
   
// Use this for initialization
   
void Start (){}
 
   
// Update is called once per frame
   
void Update ()
   {
   
RaycastHit hit;
 
   if (
Physics.Raycast(camera.ScreenPointToRay(Input.mousePosition), out hit))
   {
     if (
Input.GetMouseButtonDown(0))
     {
     }
   }
   } 
Наконец, мы должны написать код для создания префаба в точке контакта с правильной ориентацией в пространстве. Мы берем все необходимые сведения из RaycastHit (точку контакта и ориентацию точки в пространстве):

void Update ()
{
   
RaycastHit hit;
 
   if (
Physics.Raycast(camera.ScreenPointToRay(Input.mousePosition), out hit))
   {
     if (
Input.GetMouseButtonDown(0))
     {
       
Instantiate(decalPrefabhit.pointQuaternion.FromToRotation(Vector3.uphit.normal)); 
   }
   }

Теперь, если вы запустите игру, эта простая система будет работать, но raycast будет определять столкновения и с вновь созданными декалями. Чтобы избежать этого, есть простой способ: просто удалить компонент Mesh Collider префаба-декали. Это Вы должны сделать в любом случае, чтобы избежать проблем в будущем. Но, скажем, у вас есть сложный уровень с большим количеством объектов, вы должны удалить коллайдеры от всех тех объектов, на которых не должны отображаться декали, и это является не хорошим решением. Вместо этого, мы можем осуществить проверку тегов, так мы сможем определить должны ли отображаться декали на объекте.
Зайдите в редактор тегов и добавьте новый тег, назовем его "DecalOn". Теперь давайте вернемся к коду и добавим простое условие создания декали:

void Update ()
{
   
RaycastHit hit;
 
   if (
Physics.Raycast(camera.ScreenPointToRay(Input.mousePosition), out hit))
   {
       if (
hit.collider.tag == "DecalOn" && Input.GetMouseButtonDown(0))
       {
         
Instantiate(decalPrefabhit.pointQuaternion.FromToRotation(Vector3.uphit.normal));
       }
   }

Итак, теперь у нас есть три условия, которые должны быть выполнены для создания префаба-декали:
1) Луч должен пересекать объект на котором должна появиться декаль;
2) Объект должен иметь тег "DecalOn";
3) Игрок должен нажать левую кнопку мыши.

Когда все эти три условия выполняться префаб-декаль будет создан на объекте, как раз в точке контакта и с правильной ориентацией в пространстве.

Часть 2.

В этой части мы рассмотрим несколько проблем, возникающих при использовании метода с предыдущей части. Вы могли заметить одну большую проблему: на большой стене она работает отлично, но что делать если место контакта в углу объекта? Декаль будет выходить за пределы объекта:


Итак, как мы можем избежать этой проблемы? Ответ достаточно прост, но он может не удовлетворить Вас. Чтобы избежать выход декали за пределы объекта нужно выполнить проверку на уровне вершин декали: если его вершины находятся за пределами, то мы перемещаем их обратно на границу за которую они вышли. На самом деле, это довольно простая задача, но она содержит много ограничений. Прежде всего, мы не можем использовать ее в другом месте, нежели в объектах типа plane и cube. Правда, стены и колонны в играх в основном состоят из кубов различной формы и размеров, но это все равно является ограничением. Еще большее ограничение в том что объекты должны быть выравнены по осям. К тому же декали не будут работать на вращающихся объектах.

Я решил поделиться этим методом несмотря на ограничения описанные выше, потому что это может быть полезно в учебных целей, и может пригодиться для выполнения некоторых других задач. Давайте посмотрим финальную версию скрипта, а затем будем вдаваться в подробности:

using UnityEngine;
using System.Collections;
 
public class 
Decal01 MonoBehaviour 
{
   public 
GameObject decalPrefab;
 
   
void Start () 
   {
   }
  
   
void Update () 
   {
      
RaycastHit hit;
      if (
Physics.Raycast(camera.ScreenPointToRay(Input.mousePosition), out hit))
      { 
         if (
hit.collider.tag == "DecalOn" && Input.GetMouseButtonDown(0))
         
DecalGen(hit.pointhit.normalhit.collider);
      }
   }
  
   
GameObject DecalGen(Vector3 pVector3 nCollider c)
   {
      
GameObject decalInst;
   
      
decalInst = (GameObject)Instantiate(decalPrefabpQuaternion.FromToRotation(Vector3.upn));
   
      
MeshFilter mf decalInst.GetComponent(typeof(MeshFilter)) as MeshFilter;
      
Mesh m mf.mesh;
   
      
Vector3[] verts m.vertices;
   
      for (
int i 0verts.Lengthi++)
      {
         
verts[i] = decalInst.transform.TransformPoint(verts[i]);
    
         if (
verts[i].c.bounds.max.x)
         { 
            
verts[i].c.bounds.max.x;
         }
    
         if (
verts[i].c.bounds.min.x)
         { 
            
verts[i].c.bounds.min.x;
         }
    
         if (
verts[i].c.bounds.max.y)
         { 
            
verts[i].c.bounds.max.y;
         }
    
         if (
verts[i].c.bounds.min.y)
         { 
            
verts[i].c.bounds.min.y;
         }
    
         if (
verts[i].c.bounds.max.z)
         { 
            
verts[i].c.bounds.max.z;
         }
    
         if (
verts[i].c.bounds.min.z)
         { 
            
verts[i].c.bounds.min.z;
         }
         
decal mesh.verts[i] = decalInst.transform.InverseTransformPoint(verts[i]);
         
m.vertices verts;
      }
      return 
decalInst;
   }

Мы можем заметить что скрипт сильно изменился по сравнению с первой частью (он стал гораздо длиннее и сложнее): весь код создания был перенесен в новый метод, названный DecalGen. Теперь метод Update () проще, и он просто вызывает метод DecalGen когда выполняются три условия с первой части. Все функции создания декали теперь находятся в методе DecalGen. Давайте посмотрим что он делает.

Прежде всего, мы создаем новую переменную с именем "decalInst", которая будет содержать наш префаб-декаль. Она будет обработана и возвращена в конце метода.

Затем мы создаем префаб-декаль таким же способом как в первой части. Далее нам нужно взять некоторую из переменной "decalInst" информацию, для того чтобы сделать все необходимые проверки. Мы должны получить от "decalInst" MeshFilter префаба-декали, а затем его Mesh, так мы получим доступ ко всем его вершинам. Мы также создаем новый Vector3 массив для хранения вершин сетки префаба-декали и для операций на копии.

Далее идет цикл, в котором мы проходим через все вершины сетки, преобразовываем их в локальных координатах, а также проверяем отдельно каждую Vector3 компоненту (х, у и z) на выход за пределы объекта. Если ни один из них не выходит за пределы объекта, то возвращаем их на свои оси.

Затем мы преобразуем массив вершин Verts обратно в глобальное пространство, и передаем их на вершины сетки префаба-декали (мы работали с копией). Наконец, мы возвращаем наш префаб-декаль.

Если сейчас попробовать запустить игру, вы заметите, что декали никогда не будут выходить за пределы объекта. Довольно мило, да?

(Offline)
 
Ответить с цитированием
Эти 8 пользователя(ей) сказали Спасибо pie за это полезное сообщение:
Alex_Witcher (23.06.2013), cahekp (17.06.2012), Dzirt (18.06.2012), Harter (16.06.2012), HolyDel (16.06.2012), is.SarCasm (16.06.2012), Nuprahtor (16.06.2012), pax (20.06.2012)
Старый 16.06.2012, 16:17   #2
is.SarCasm
Бывалый
 
Аватар для is.SarCasm
 
Регистрация: 17.05.2009
Адрес: Днепропетровск
Сообщений: 672
Написано 180 полезных сообщений
(для 427 пользователей)
Ответ: Декали в Unity3D

Огромный респект! Как раз то, что мне сейчас нужно!
(Offline)
 
Ответить с цитированием
Старый 17.06.2012, 08:52   #3
IGR
Blitz's Shame !!
 
Регистрация: 31.03.2007
Сообщений: 3,639
Написано 832 полезных сообщений
(для 2,012 пользователей)
Ответ: Декали в Unity3D

в апдейте лучше проверку на нажатие мышки вынисти на самый верхний уровень, что бы проверять рейкаст только по клику а не каждый апдейт !!
(Offline)
 
Ответить с цитированием
Эти 3 пользователя(ей) сказали Спасибо IGR за это полезное сообщение:
Dzirt (17.06.2012), is.SarCasm (17.06.2012), pax (20.06.2012)
Старый 16.03.2016, 08:57   #4
arsone
AnyKey`щик
 
Регистрация: 16.03.2016
Сообщений: 1
Написано 0 полезных сообщений
(для 0 пользователей)
Ответ: Декали в Unity3D

А не правильнее ли:
Instantiate(decalPrefab, hit.point, Quaternion.FromToRotation(-decalPrefab.transform.up, hit.normal));

Иначе поворачиваться будет тот объект, в котором находится этот скрипт, а не создаваемый из префаба объект.
(Offline)
 
Ответить с цитированием
Старый 16.03.2016, 09:41   #5
Антихрист
Разработчик
 
Регистрация: 20.01.2007
Сообщений: 477
Написано 176 полезных сообщений
(для 404 пользователей)
Ответ: Декали в Unity3D

Сообщение от arsone Посмотреть сообщение
А не правильнее ли:
Instantiate(decalPrefab, hit.point, Quaternion.FromToRotation(-decalPrefab.transform.up, hit.normal));

Иначе поворачиваться будет тот объект, в котором находится этот скрипт, а не создаваемый из префаба объект.
Привет,некромант,Нет, все параметры внутри Instantiate относятся уже к объекту который инстантиэитится + Vector3.up и transform.up могут быть разными.
(Offline)
 
Ответить с цитированием
Старый 21.04.2016, 18:49   #6
Evgen
Знающий
 
Аватар для Evgen
 
Регистрация: 12.01.2011
Адрес: Moscow capital of Earth! :)
Сообщений: 277
Написано 32 полезных сообщений
(для 45 пользователей)
Ответ: Декали в Unity3D

Очень неплохо для начала. Но можно использовать бесплатный ассет из ассетстора:

Simple decal system

https://www.assetstore.unity3d.com/en/#!/content/13889



Качаем и используем. Очень удобно.
(Offline)
 
Ответить с цитированием
Ответ


Опции темы

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.


Часовой пояс GMT +1, время: 08:38.


vBulletin® Version 3.6.5.
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.
Перевод: zCarot
Style crйe par Allan - vBulletin-Ressources.com