Реализация эффекта toon с помощью Metal shader и SceneKit - PullRequest
0 голосов
/ 06 декабря 2018

Я хотел бы улучшить свой недавний вопрос и реализовать мультяшный эффект вот так , используя шейдер Metal (фрагмент) в SceneKit.

Вот моя реализация шейдера фрагментаи простое освещение фонгоном и эффект мультяшки:

fragment float4 lightingFragment(VertexOut in [[stage_in]]) {

    float3 normal = normalize(in.normal);

    // For edges set color to yellow
    float3 V = normalize(in.eye - in.position.xyz);
    float edgeDetection = (abs(dot(V, normal)) > 0.1) ? 1 : 0;
    if ( edgeDetection != 1 ) {
        return float4(1, 1, 0, 1);
    }

    // Compute simple phong
    float3 lightDirection = normalize(light.position - in.position.xyz);
    float diffuseIntensity = saturate(dot(normal, lightDirection));
    float3 diffuseTerm = light.diffuseColor * material.diffuseColor * diffuseIntensity;

    // Ambient color
    float3 ambientTerm = light.ambientColor * material.ambientColor;

    return float4(ambientTerm + diffuseTerm, 1.0);
}

Как я уже сказал, я вдохновляюсь этой статьей , но я получаю совсем другой результат ...

Есть идеи?Вот весь проект

1 Ответ

0 голосов
/ 07 декабря 2018

Этот тип однопроходной техники в пространстве обзора обязательно приведет к худшим результатам, чем метод обычной экструзии, но для того, чтобы он вообще работал, вам нужно получить контроль над своими координатными пространствами.

Какова цель здесь?Что ж, мы хотим заметить, когда нормаль поверхности почти перпендикулярна к направлению обзора, и обойти наши обычные расчеты освещения, вместо этого возвращая сплошной цвет силуэта.

Определение перпендикулярности обычно означает взятие точкипродуктов, но в (+ + вверх, правша) поле зрения, направление просмотра просто (0, 0, -1), V просто (0, 0, 1), и произведение точек между Vи нормаль пространства-представления является просто компонентом z нормали пространства-представления.

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

Во-первых, измените имя нормали, которую мы выделяем из вершинного шейдера, на eyeNormal, чтобы мы знали, в каком пространстве мы работаем. Затем он вычисляется как

out.eyeNormal = (scn_node.modelViewTransform * float4(in.normal, 0)).xyz;

(где мы делаем простое предположение, что матрица MV не содержит неравномерного масштабирования или сдвига).Нормализовать как обычно в фрагментном шейдере:

float3 normal = normalize(in.eyeNormal);

Избавиться от этого троичного мусора;логическое выражение имеет логический тип, который просто прекрасно преобразуется в float:

float edgeFactor = normal.z <= 0.3;

Обратите внимание, что, поскольку мы работаем в пространстве представления, на самом деле мы не увидим фрагменты, имеющие normal.z < 0таким образом, мы также отбрасываем abs.

Наконец, поскольку мы не будем сохранять какие-либо циклы с таким ранним выходом, мы можем использовать функцию mix для выбора между освещенным цветом и цветом краякак мы вернемся:

return mix(float4(ambientTerm + diffuseTerm, 1.0), float4(1, 1, 0, 1), edgeFactor);

И вот, у вас это есть, грубые силуэты за полцены:

Screenshot

...