卡通漫画网点波点渲染效果shader

        在这篇技术指导中,我们将介绍一种节省性能且简单的方式来实现卡通网点效果。然而,这种方法在模型表面特别平直时效果可能不佳。

实现原理

        如下图所示,我们使用本地坐标或世界坐标作为距离来计算一个点阵,并利用模型的表面与这些点阵相切,再将相切的结果绘制到模型表面上。具体步骤如下:

  1. 计算波点位置:通过模型空间坐标或世界空间坐标计算波点的位置。
  2. 确定波点范围:根据光照方向和表面法线,计算波点的范围。
  3. 应用颜色贴图:使用 ramp texture 对计算好的波点和光方向进行着色。
代码片段

代码片段

        我们这里使用的是模型空间坐标来计算波点。

                float vertexPoint = length(frac(i.positionOS.xyz * _DotSize) - (0.5).xxx );
  • i.positionOS 是顶点在模型空间中的位置。
  • _DotSize 控制波点的大小。
  • frac 函数返回输入值的小数部分,用于生成重复的网格图案。
  • length 函数计算从中心到当前顶点的小数部分距离,形成圆形波点。

        接下来,我们计算需要波点的范围:

float halfLambert = dot(_MainLightPosition.xyz,i.normalWS) * 0.5 + 0.5;
float externalScale = 1.0 - smoothstep(_ExternalOffset, _ExternalOffset + _ExternalSoft, halfLambert);
float centerScale = smoothstep(_CenterOffset, _CenterOffset + _CenterSoft, halfLambert);
float pointPower = externalScale * centerScale;
float pointMask = round(pow(vertexPoint, pointPower));
  • halfLambert 是基于光照方向和表面法线计算出的半 Lambert 光照值,用于模拟光照强度。
  • _MainLightPosition 是主光源的位置。
  • smoothstep 函数用于平滑过渡,使得外部和中心区域有柔和的渐变效果。
  • externalScale 和 centerScale 控制波点外部和中心区域的范围及过渡。
  • pointPower 是外部和中心范围的乘积,用于调节波点强度。
  • pointMask 使用 round(pow(vertexPoint, pointPower)) 来生成最终的波点遮罩。

        

        最后,使用 ramp texture 对计算好的波点和光方向进行着色:

float2 colorUV = lerp( float2(_PointColorRange, 0.1), float2(halfLambert,0.1 ),  pointMask);
float3 finalColor = tex2D(_RampTex, colorUV);
  • lerp 函数用于插值,根据 pointMask 在两个颜色之间进行线性插值。
  • _PointColorRange 控制波点颜色调节范围。
  • tex2D 函数从贴图中采样颜色。

完整代码(HLSL)

Shader "Unlit/PointArt_Code"
{
    Properties
    {
        [SingleLineTexture]_RampTex ("颜色贴图", 2d) = "white" {}
        _PointColorRange ("波点颜色调节", range(0.0, 1.0)) = 0.1
        
        _DotSize ("波点大小" , float) = 1
        
        _ExternalOffset("波点外部范围调整", Range(0.0, 1.0)) = 0.3
        _ExternalSoft("波点外部范围过度", Range(0.0, 1.0)) = 0.2
        
        _CenterOffset ("波点中间范围调整", Range(0.0, 1.0)) = 0.1   
        _CenterSoft ("波点中间范围过度", Range(0.0, 1.0)) = 0.1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        
        Pass
        {
            Name "Forward"
            Tags {"LightMode" = "UniversalForwardOnly" }
            
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"


            struct appdata
            {
                float4 positionOS : POSITION;
                float3 normalOS : NORMAL;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float4 positionOS : TEXCOORD0 ;
                float3 normalWS : TEXCOORD1 ;
                
            };

            CBUFFER_START(UnityPerMaterial)
            float _DotSize;

            float _ExternalOffset;
            float _ExternalSoft;

            float _CenterOffset;
            float _CenterSoft;

            float _PointColorRange;
            CBUFFER_END

            sampler2D _RampTex;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = TransformObjectToHClip(v.positionOS);
                o.positionOS = v.positionOS;
                
                o.normalWS = TransformObjectToWorldNormal(v.normalOS) ; 
                return o;
            }

            float4 frag (v2f i) : SV_Target
            {
                //使用模型空间坐标计算波点,根据需要是可以使用世界空间的坐标。
                float vertexPoint = length(frac(i.positionOS.xyz * _DotSize) - (0.5).xxx );
                //计算圆点的范围
                float halfLambert = dot(_MainLightPosition.xyz,i.normalWS) * 0.5 + 0.5;
                float externalScale = 1.0 - smoothstep(_ExternalOffset, _ExternalOffset + _ExternalSoft, halfLambert);
                float centerScale = smoothstep(_CenterOffset, _CenterOffset + _CenterSoft, halfLambert);
                float pointPower = externalScale * centerScale;
                float pointMask = round(pow(vertexPoint, pointPower));
                //采样贴图颜色
                float2 colorUV = lerp( float2(_PointColorRange, 0.1), float2(halfLambert,0.1 ),  pointMask);
                float3 finalColor = tex2D(_RampTex, colorUV);
                
                return float4(finalColor,1);
            }
            ENDHLSL
        }
    }
    CustomEditor "UnityEditor.ShaderGraphUnlitGUI"
    FallBack "Hidden/Shader Graph/FallbackError"

}

总结

以上代码展示了如何通过简单且高效的方法实现卡通网点效果。这种方法利用了模型空间或世界空间的坐标,通过对光照方向和表面法线的计算,将网点效果映射到模型表面。尽管在模型特别平直时效果可能不佳,但总体上是一种性能友好的实现方式。

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕业设计,基于SpringBoot+Vue+MySQL开发的体育馆管理系统,源码+数据库+毕业论文+视频演示 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本体育馆管理系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,达到事半功倍的效果。此体育馆管理系统利用当下成熟完善的SpringBoot框架,使用跨平台的可开发大型商业网站的Java语言,以及最受欢迎的RDBMS应用软件之一的Mysql数据库进行程序开发。实现了用户在线选择试题并完成答题,在线查看考核分数。管理员管理收货地址管理、购物车管理、场地管理、场地订单管理、字典管理、赛事管理、赛事收藏管理、赛事评价管理、赛事订单管理、商品管理、商品收藏管理、商品评价管理、商品订单管理、用户管理、管理员管理等功能。体育馆管理系统的开发根据操作人员需要设计的界面简洁美观,在功能模块布局上跟同类型网站保持一致,程序在实现基本要求功能时,也为数据信息面临的安全问题提供了一些实用的解决方案。可以说该程序在帮助管理者高效率地处理工作事务的同时,也实现了数据信息的整体化,规范化与自动化。 关键词:体育馆管理系统;SpringBoot框架;Mysql;自动化
要在Canvas上创建流动波点效果,你可以使用JavaScript来实现。下面是一个简单的示例代码: ```html <!DOCTYPE html> <html> <head> <title>流动波点</title> <style> canvas { border: 1px solid black; } </style> </head> <body> <canvas id="myCanvas" width="400" height="400"></canvas> <script> const canvas = document.getElementById("myCanvas"); const context = canvas.getContext("2d"); const numDots = 100; // 波点数量 const radius = 2; // 波点半径 const speed = 0.05; // 波点移动速度 let phase = 0; function draw() { context.clearRect(0, 0, canvas.width, canvas.height); for (let i = 0; i < numDots; i++) { const angle = (i / numDots) * Math.PI * 2; // 计算角度 const x = (canvas.width / 2) * (1 + Math.cos(angle * 5 + phase) * Math.sin(angle * 3 + phase * 2)) + Math.cos(phase * 2) * (i * 2); // 计算x坐标 const y = (canvas.height / 2) * (1 + Math.sin(angle * 5 + phase) * Math.cos(angle * 3 + phase * 2)) + Math.sin(phase * 2) * (i * 2); // 计算y坐标 context.beginPath(); context.arc(x, y, radius, 0, Math.PI * 2); context.fillStyle = "blue"; context.fill(); } phase += speed; requestAnimationFrame(draw); } draw(); </script> </body> </html> ``` 这段代码会在一个400x400的Canvas上创建100个流动的蓝色波点效果。你可以根据需要调整波点的数量、半径和移动速度,以及Canvas的大小。通过在`draw`函数中计算每个波点的坐标,并使用`context.arc`方法绘制圆形,实现了流动波点效果。整个动画使用了requestAnimationFrame进行循环绘制。你可以将上述代码复制到HTML文件中运行,查看效果
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值