unity NPR 卡通渲染

news2024/10/7 6:32:46

文章目录

  • 一、 介绍
  • 二、 素材准备
  • 三、 步骤
  • 四、 shader代码
  • 五、工程链接


一、 介绍

NPR是计算机图形学中的一类,即非真实感绘制(Non-photorealistic rendering),主要用于模拟艺术式的绘制风格,也用于发展新绘制风格,形式一般是卡通造影。

NPR是Unity中的一种非真实渲染技术,它使用一种称为"NPR"的算法来模拟非真实渲染效果。这种技术可以用于制作各种类型的视觉效果,包括卡通效果、手绘效果、水彩画效果等等。

NPR算法的核心思想是将3D模型表面的每个点映射到一个二维平面上,这个平面上的每个像素点对应一个颜色。然后,NPR算法会根据这个颜色和周围像素点的颜色来计算这个像素点的最终颜色。这个过程可以看作是在这个二维平面上进行一次扫描,并且根据扫描到的每个像素点的颜色和周围像素点的颜色来生成最终的渲染结果。

NPR技术的优点在于它可以在保证画面美观的前提下,极大地降低了渲染的计算量和硬件要求,使得游戏能够在较低配置的设备上运行。此外,NPR技术还可以用于制作各种类型的视觉效果,使得游戏画面更加丰富和多样化。

本文将介绍如何将一个原始的3D角色通过NPR渲染技术转换成类似《原神》中的卡通角色。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


二、 素材准备


三、 步骤

前期角色外观:
在这里插入图片描述
在这里插入图片描述
显示纹理:
在这里插入图片描述
描边:
在这里插入图片描述
高光和边缘光:

在这里插入图片描述
最终渲染效果:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


四、 shader代码

Shader "Custom/ToonShader"
{
    Properties {
        [NoScaleOffset] _MainTex ("MainTex", 2D) = "white" {} 
        // 主纹理,用于对象的颜色贴图
        _MainColor ("MainColor", Color) = (1,1,1,1)
        // 主颜色,用于设置对象的主要颜色
        _OutLineWidth ("OutlLineWidth", Range(0, 0.1)) = 0.002
        // 描边宽度
        _OutLineColor ("OutLineColor", Color) = (0,0,0,1)
        // 描边颜色
        [NoScaleOffset] _GradientTex ("GradientTex", 2D) = "white" {}
        // 用于阴影梯度采样的纹理,暂时就叫这个名词吧
        _GradientIntensity ("GradientIntensity", Range(0,1)) = 1
        // 阴影梯度采样纹理强度
        _SpecularPower ("SpecularPower", Range(1,100)) = 80
        // 高光平滑度
        _SpecularIntensity ("SpecularItensity", Range(0,1)) = 1
        // 高光强度
        _SpecularThreshold ("SpecularThreshold", Range(0,1)) = 0.3
        // 高光阈值
        _SpecularBrightness ("SpecularBrightness", Range(0,1)) = 0.1
        // 高光添加的亮度量
        _SpecularValue ("SpecularValue", Range(0,1)) = 1
        // 高光值
        _RimIntensity ("RimIntensity", Range(0,5)) = 1
        // 边缘光强度
        [Toggle] _RimShowAtBackToLight ("RimShowAtBackToLight", Float) = 0
        // 边缘光是否被光照时才显示的开关
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 100
        Pass { // solid
            Name "Solid"
            Tags { "LightMode"="ForwardBase" }
            // 阴影需要,正向渲染光照基础pass
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "AutoLight.cginc"
            #pragma multi_compile_fwdbase_fullshadows
            // 阴影需要
            #pragma fragmentoption ARB_precision_hint_fastest
            // 最快速精度

            struct appdata {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                half3 normal : NORMAL;
            };

            struct v2f {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
                half3 worldNormal : TEXCOORD1;
                float3 worldPos : TEXCOORD2;
                UNITY_LIGHTING_COORDS(3,4)
                // 阴影需要
            };

            sampler2D _MainTex;
            fixed4 _MainColor;
            sampler2D _GradientTex;
            fixed _GradientIntensity;
            fixed _SpecularPower;
            fixed _SpecularIntensity;
            fixed _SpecularThreshold;
            fixed _SpecularBrightness;
            fixed _SpecularValue;
            fixed _RimIntensity;
            fixed _RimShowAtBackToLight;

            v2f vert (appdata v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                o.worldNormal = UnityObjectToWorldNormal(v.normal);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex);
                TRANSFER_VERTEX_TO_FRAGMENT(o)
                // 阴影需要
                return o;
            }

            fixed4 frag (v2f i) : SV_Target {
                i.worldNormal = normalize(i.worldNormal);
                half3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos);
                half3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos);
                atten = atten * 0.5 + 0.5;
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
                fixed LdotN = dot(lightDir, i.worldNormal);
                fixed halfLambert = LdotN * 0.5 + 0.5;
                fixed lightShadowCoef = halfLambert * atten;
                fixed3 diffuse = tex2D(_MainTex, i.uv).rgb * _MainColor;
                fixed3 dGradient = tex2D(_GradientTex, float2(lightShadowCoef, 0)).rgb;
                diffuse = lerp(diffuse,  diffuse * dGradient, _GradientIntensity);
                half3 hDir = normalize(viewDir + lightDir);
                fixed HdotN = max(0, dot(hDir, i.worldNormal));
                fixed specular = pow(HdotN, _SpecularPower) * _SpecularIntensity;
                specular *= atten;
                specular = step(_SpecularThreshold, specular) * _SpecularValue;
                fixed rimFactor = (1 - dot(viewDir, i.worldNormal)) * _RimIntensity;
                rimFactor = lerp(rimFactor, rimFactor * max(0, dot(-lightDir, viewDir)), _RimShowAtBackToLight);
                rimFactor *= atten;
                rimFactor = step(_SpecularThreshold, rimFactor) * _SpecularValue;
                specular = max(specular, rimFactor);
                return fixed4(
                    ambient + 
                    diffuse + 
                    diffuse * specular + specular * _SpecularBrightness
                    , 1);
            }
            ENDCG
        }

        Pass { // outline
            Name "Outline"
            Cull Front
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #pragma fragmentoption ARB_precision_hint_fastest
            fixed _OutLineWidth;
            fixed4 _OutLineColor;
            float4 vert (float4 vertex : POSITION, float3 normal : NORMAL) : SV_POSITION {
                // 投影空间
                float4 pos = UnityObjectToClipPos(vertex);
                // 转换到视图空间的法线
                fixed3 vNormal = mul((float3x3)UNITY_MATRIX_IT_MV, normal);
                fixed2 offset = TransformViewToProjection(vNormal.xy);
                // 由于在顶点后处理会有透视除法,所以我们首先乘上pos.w以抵消透视,这样无论多远多近都可以按恒定的描边边宽来显示
                pos.xy += offset * _OutLineWidth * pos.w;
                return pos;
            }
            fixed4 frag () : SV_Target { return _OutLineColor; }
            ENDCG
        }
    }
    Fallback "Diffuse"
}

这个Unity卡通渲染的Shader代码使用了以下技术和功能:

  1. 主纹理和颜色属性:使用了主纹理 _MainTex 和主颜色 _MainColor 来控制对象的颜色。

  2. 描边效果:通过 _OutLineWidth_OutLineColor 控制描边的宽度和颜色。

  3. 阴影梯度采样:使用 _GradientTex_GradientIntensity 控制阴影梯度采样纹理和强度。

  4. 高光效果:通过 _SpecularPower_SpecularIntensity_SpecularThreshold_SpecularBrightness_SpecularValue 控制高光的平滑度、强度、阈值和亮度。

  5. 边缘光效果:使用 _RimIntensity 控制边缘光的强度,以及 _RimShowAtBackToLight 控制边缘光是否受光照方向影响。

  6. 正向渲染光照:使用 TagsCGPROGRAM 中的相关指令来支持正向渲染光照,包括阴影的处理。

  7. 顶点着色器和片元着色器:定义了顶点着色器 vert 和片元着色器 frag,并在这里执行了主要的渲染计算。

  8. 光照计算:计算了光照方向、光照强度、阴影、漫反射、高光等光照效果。

  9. 描边效果的后处理:在 Outline Pass 中,实现了对象的描边效果。

  10. Shader Fallback:如果渲染模式不支持这个Shader,会回退到使用 “Diffuse” Shader。

这个Shader包含了多种效果,包括卡通风格的渲染、高光、描边和阴影梯度,以及一些参数控制这些效果的强度和外观。这些效果可以用于实现卡通风格的游戏或动画渲染。


五、工程链接


https://download.csdn.net/download/qq_20179331/88454844

内含exe文件

1.滑动鼠标中键滚轮,可以放大、缩小视角

2.按一下q键,场景相机顺时针旋转90度;按一下w键,场景相机逆时针旋转90度。

在这里插入图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1118221.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

零基础Linux_20(进程信号)内核态和用户态+处理信号+不可重入函数+volatile

目录 1. 内核态和用户态 1.1 内核态和用户态概念 1.2 内核态和用户态转化 2. 处理信号 2.2 捕捉信号 2.2 系统调用sigaction 3. 不可重入函数 4. volatile关键字 5. SIGCHLD信号(了解) 6. 笔试选择题 答案及解析 本篇完。 1. 内核态和用户态…

气象台使用vr模拟仿真实训教学降低成本投入

气候仿真实验室用于模拟高低温、高湿、干燥、阳光光照、降雨、降雪、覆冰、雾天与强风等多种环境适应性试验等气候和环境条件,在环境试验中,温度、湿度、光照、降雨这些常见的仿真环境都很容易实现。而比较少见的雾天、强风、降雪等环境就比较难。因此为…

《动手学深度学习 Pytorch版》 9.8 束搜索

本节将介绍几大: 贪心搜索(greedy search)策略 穷举搜索(exhaustive search) 束搜索(beam search) 9.8.1 贪心搜索 贪心搜索已用于上一节的序列预测。对于输出序列的每一时间步 t ′ t t′…

【FPGA零基础学习之旅#16】嵌入式块RAM-双口ram的使用

🎉欢迎来到FPGA专栏~双口ram的使用 ☆* o(≧▽≦)o *☆嗨~我是小夏与酒🍹 ✨博客主页:小夏与酒的博客 🎈该系列文章专栏:FPGA学习之旅 文章作者技术和水平有限,如果文中出现错误,希望大家能指正…

SystemVerilog学习(3)——数组

一、定宽数组 相比于Verilog-1995中的一维定宽数组,SV提供了更加多样的数组类型,功能上也大大增强。 1.1 定宽数组的声明与初始化 Verliog要求在声明中必须给出数组的上下界。因为几乎所有的数组都使用0作为索引下届,所以SV允许只给出数组的…

UART、SPI、I2C通信协议超全入门教程

本文引注: https://mp.weixin.qq.com/s/lVWK8xlDt7cOLi8WHYSuPg 1.SPI协议 1.基础 2.简介 3.工作原理 4.SPI数据传输步骤与优缺点 2.UART协议

抖音同城榜:探索城市新潮流

随着科技的飞速发展,短视频已经成为了人们日常生活中不可或缺的一部分。作为短视频领域的佼佼者,抖音一直致力于为用户带来更丰富、更有趣的短视频内容。抖音同城榜应运而生,成为了最新、最热门的话题聚集地,吸引了大量潮流达人和…

Java SSL/TLS证书认证逻辑

前言 最近做项目使用httpclient转发https请求,但是遇到一些坑,尤其是证书的认证,证书认证一般都是单向的,除非相互访问,证书一般基于host,但是如果访问需要ip,那么JDK默认的认证就会不通过&…

Unity⭐️Win和Mac安卓打包环境配置

文章目录 🟥 配置Android SDK1️⃣ 配置 SDK Platforms2️⃣ 配置 SDK Tools🎁 Android SDK Build-Tools🎁 Android SDK Command-line Tools(latest)🎁 Android SDK Tools(Obsolete)🟧 配置NDK🟩 配置JDK前情提示: 此方法适用于Windows/Mac 在配置时注意开启 🪜 …

shell变量之学习笔记

shell变量之学习笔记 Shell变量概念1 shell变量分类:2 变量的赋值3 变量赋值格式:4 变量命名方式5 变量声明6 变量引用7 变量清除8 变量只读9 内部参数变量10 位置参数变量11 退出和返回状态12 命令替换13 read命令14 字符串长度与截取15 字符串替换16 变…

FL studio21永久激活码 附带一键下载安装包

玩音乐的朋友,对FL studio肯定不陌生,目前最新的版本是FL studio21,这是一款非常强大且专业的音频制作软件,而且还可以编曲、剪辑、录音、混音等等之类的创作操作,使你的计算机成为一个全功能录音室。下面小编就来和大…

15.Tensor Product vs. Kronecker Product

此处是为澄清她两的区别, 这两个事物 都用的相同的符号表示, 即这个圈数符号, 它在数学中有许多不同的含义, 本文讨论的是:“张量积”,它是对张量的运算; 以及“Kronecker ”积,…

C#WPFPrism框架模块化应用实例

本文实例演示C#WPFPrism框架模块化应用实例。 首先创建WPF项目,修改App相关文件内容,以便使用prism。 一、添加模块 解决方案中添加ModuleA和ModuleB两个项目 删除不用的窗体,添加自定义窗体 修改ModuleA和ModuleB的输出类型

最近又火了!吴恩达《生成式 AI》重磅发布!

吴恩达教授可能是许多人接触 AI 的启蒙课导师吧,在过去的十多年中,他的《Machine Learning》课程已经对数百万的学习者产生了积极影响。 而随着 ChatGPT 的推出,大模型和各类生成式人工智能(GenAI)技术在行业内外备受…

激活WinEdt 11.1

激活WinEdt 11.1 打开WinEdt 11.1,点击help 点击Register WinEdtname输入:*60[20220501] Warez_Down [RU-BOARD] (100 users)Code输入:4049089118892183088 即可激活。 参考文章https://blog.csdn.net/j_l_sheng/article/details/125456662

内存的基础知识

一、概述 1.内存: 存放数据,为了缓和CPU与硬盘之间的速度差异,程序执行前会先放入内存中再让CPU处理。 由存储单元(存放数据的最小单元,每个单元都会对应一个地址)构成 2.常用数量单位: 1K(千)2的10次方 1M(兆)2的20次…

基于springboot实现藏区特产销售平台项目【项目源码+论文说明】计算机毕业设计

基于springboot实现藏区特产销售平台演示 摘要 “互联网”的战略实施后,很多行业的信息化水平都有了很大的提升。但是目前很多藏区特产销售信息仍是通过人工管理的方式进行,需要在各个岗位投入大量的人力进行很多重复性工作,使得对人力物力造…

Python Django教程之实现待办事项应用程序

Django是一个基于Python Web框架的高级Web框架,允许快速开发和干净,务实的设计。本文将创建一个待办事项应用程序,以了解Django的基础知识,感兴趣的可以尝试一下。今天,我们将创建一个待办事项应用程序,以了…

基于springboot实现4S店车辆管理系统项目【项目源码+论文说明】

基于springboot实现4S店车辆管理系统演示 摘要 随着信息技术和网络技术的飞速发展,人类已进入全新信息化时代,传统管理技术已无法高效,便捷地管理信息。为了迎合时代需求,优化管理效率,各种各样的管理系统应运而生&am…

leetcode 105. 从前序与中序遍历序列构造二叉树

2023.10.21 本题需要根据前序遍历序列和中序遍历序列来构造出一颗二叉树。类似于从中序与后序遍历序列构造二叉树 。使用递归, java代码如下: /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* …