Catlike Coding Custom SRP笔记 - 平行光

news/2025/1/21 23:41:47/文章来源:https://www.cnblogs.com/sailJs/p/18617601

原文链接:Directional Lights

 

效果图

 

CustomRenderPipelineAsset.cs

[CreateAssetMenu(menuName = "Rendering/Custom Render Pipeline")]
public class CustomRenderPipelineAsset : RenderPipelineAsset {public bool useDynamicBatching = true; //启用动态合批public bool useGPUInstancing = true; //GPU Instancingpublic bool useSRPBatcher = true; //SRP合批protected override RenderPipeline CreatePipeline () {return new CustomRenderPipeline(useDynamicBatching, useGPUInstancing, useSRPBatcher);}}

 

CustomRenderPipeline.cs

public class CustomRenderPipeline : RenderPipeline {CameraRenderer renderer = new CameraRenderer();bool useDynamicBatching, useGPUInstancing;public CustomRenderPipeline (bool useDynamicBatching, bool useGPUInstancing, bool useSRPBatcher) {this.useDynamicBatching = useDynamicBatching;this.useGPUInstancing = useGPUInstancing;GraphicsSettings.useScriptableRenderPipelineBatching = useSRPBatcher;GraphicsSettings.lightsUseLinearIntensity = true;}protected override void Render(ScriptableRenderContext context, Camera[] cameras) {}protected override void Render(ScriptableRenderContext context, List<Camera> cameras) {for (int i = 0; i < cameras.Count; i++) {renderer.Render(context, cameras[i], useDynamicBatching, useGPUInstancing);}}}

 

CameraRenderer.cs

public partial class CameraRenderer {const string bufferName = "Render Camera";static ShaderTagId unlitShaderTagId = new ShaderTagId("SRPDefaultUnlit");static ShaderTagId litShaderTagId = new ShaderTagId("CustomLit");CommandBuffer buffer = new CommandBuffer {name = bufferName};ScriptableRenderContext context;Camera camera;CullingResults cullingResults;Lighting lighting = new Lighting();public void Render(ScriptableRenderContext context, Camera camera,bool useDynamicBatching, bool useGPUInstancing) {this.context = context;this.camera = camera;#if UNITY_EDITORPrepareBuffer();PrepareForSceneWindow();
#endifif (!Cull()) {return;}Setup();lighting.Setup(context, cullingResults);DrawVisibleGeometry(useDynamicBatching, useGPUInstancing);
#if UNITY_EDITORDrawUnsupportedShaders();DrawGizmos();
#endifSubmit();}bool Cull() {if (camera.TryGetCullingParameters(out ScriptableCullingParameters p)) {cullingResults = context.Cull(ref p);return true;}return false;}void Setup() {context.SetupCameraProperties(camera);CameraClearFlags flags = camera.clearFlags;buffer.ClearRenderTarget(flags <= CameraClearFlags.Depth,flags <= CameraClearFlags.Color,(flags == CameraClearFlags.Color) ? camera.backgroundColor.linear : Color.clear);buffer.BeginSample(SampleName);ExecuteBuffer();}void Submit() {buffer.EndSample(SampleName);ExecuteBuffer();context.Submit();}void ExecuteBuffer() {context.ExecuteCommandBuffer(buffer);buffer.Clear();}void DrawVisibleGeometry(bool useDynamicBatching, bool useGPUInstancing) {//先渲染不透明物体var sortingSettings = new SortingSettings(camera) {criteria = SortingCriteria.CommonOpaque};var drawingSettings = new DrawingSettings(unlitShaderTagId, sortingSettings) {enableDynamicBatching = useDynamicBatching,enableInstancing = useGPUInstancing};drawingSettings.SetShaderPassName(1, litShaderTagId);var filteringSettings = new FilteringSettings(RenderQueueRange.opaque);context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings);//再渲染天空盒
        context.DrawSkybox(camera);//再渲染半透明物体sortingSettings.criteria = SortingCriteria.CommonTransparent;drawingSettings.sortingSettings = sortingSettings;filteringSettings.renderQueueRange = RenderQueueRange.transparent;context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings);}}

 

CameraRenderer.Edtor.cs

partial class CameraRenderer {#if UNITY_EDITOR//内置渲染管线的tag标识static ShaderTagId[] legacyShaderTagIds = {new ShaderTagId("Always"),new ShaderTagId("ForwardBase"),new ShaderTagId("PrepassBase"),new ShaderTagId("Vertex"),new ShaderTagId("VertexLMRGBM"),new ShaderTagId("VertexLM")};static Material errorMaterial;string SampleName { get; set; }void DrawGizmos() {if (Handles.ShouldRenderGizmos()) {context.DrawGizmos(camera, GizmoSubset.PreImageEffects);context.DrawGizmos(camera, GizmoSubset.PostImageEffects);}}//shader错误时, 显示为粉红void DrawUnsupportedShaders() {if (errorMaterial == null) {errorMaterial = new Material(Shader.Find("Hidden/InternalErrorShader"));}var drawingSettings = new DrawingSettings(legacyShaderTagIds[0], new SortingSettings(camera)) {overrideMaterial = errorMaterial};for (int i = 1; i < legacyShaderTagIds.Length; i++) {drawingSettings.SetShaderPassName(i, legacyShaderTagIds[i]);}var filteringSettings = FilteringSettings.defaultValue;context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings);}void PrepareForSceneWindow() {if (camera.cameraType == CameraType.SceneView) {ScriptableRenderContext.EmitWorldGeometryForSceneView(camera); //渲染ugui的几何图形
        }}void PrepareBuffer() {Profiler.BeginSample("Editor Only"); //Window -> Analysis -> Profile -> CPU Usage:Hierarchy中会显示buffer.name = SampleName = camera.name; //编辑器下BeginSample使用相机名字, Player下使用固定名字(Render Camera)
        Profiler.EndSample();}#elseconst string SampleName = bufferName;
#endif}

 

Lighting.cs

public class Lighting {const string Sample_Name = "Lighting";const int maxDirLightCount = 4;static int dirLightCountId = Shader.PropertyToID("_DirectionalLightCount");static int dirLightColorsId = Shader.PropertyToID("_DirectionalLightColors");static int dirLightDirectionsId = Shader.PropertyToID("_DirectionalLightDirections");static Vector4[] dirLightColors = new Vector4[maxDirLightCount];static Vector4[] dirLightDirections = new Vector4[maxDirLightCount];CommandBuffer buffer = new CommandBuffer {name = "Lighting"};CullingResults cullingResults;public void Setup(ScriptableRenderContext context, CullingResults cullingResults) {this.cullingResults = cullingResults;buffer.BeginSample(Sample_Name);SetupLights();buffer.EndSample(Sample_Name);context.ExecuteCommandBuffer(buffer);buffer.Clear();}void SetupLights() {NativeArray<VisibleLight> visibleLights = cullingResults.visibleLights;int dirLightCount = 0;for (int i = 0; i < visibleLights.Length; i++) {VisibleLight visibleLight = visibleLights[i];if (visibleLight.lightType == LightType.Directional) {SetupDirectionalLight(dirLightCount++, ref visibleLight);if (dirLightCount >= maxDirLightCount) break;}}buffer.SetGlobalInt(dirLightCountId, dirLightCount);buffer.SetGlobalVectorArray(dirLightColorsId, dirLightColors);buffer.SetGlobalVectorArray(dirLightDirectionsId, dirLightDirections);}void SetupDirectionalLight (int index, ref VisibleLight visibleLight) {dirLightColors[index] = visibleLight.finalColor;dirLightDirections[index] = -visibleLight.localToWorldMatrix.GetColumn(2);}}

 

ShaderLibrary/UnityInput.hlsl

#ifndef CUSTOM_UNITY_INPUT_INCLUDED
#define CUSTOM_UNITY_INPUT_INCLUDEDCBUFFER_START(UnityPerDraw)float4x4 unity_ObjectToWorld;float4x4 unity_WorldToObject;float4 unity_LODFade;real4 unity_WorldTransformParams;
CBUFFER_ENDfloat4x4 unity_MatrixVP; //世界空间坐标转裁剪空间
float4x4 unity_MatrixV; //世界空间坐标转相机空间
float4x4 unity_MatrixInvV;
float4x4 unity_prev_MatrixM;
float4x4 unity_prev_MatrixIM;
float4x4 glstate_matrix_projection; //投影矩阵

float3 _WorldSpaceCameraPos; //相机位置#endif

 

ShaderLibrary/Common.hlsl

#ifndef CUSTOM_COMMON_INCLUDED
#define CUSTOM_COMMON_INCLUDED#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl"
#include "UnityInput.hlsl"//下面的宏是SpaceTransforms会用到, 不定义无法通过编译
#define UNITY_MATRIX_M unity_ObjectToWorld
#define UNITY_MATRIX_I_M unity_WorldToObject
#define UNITY_MATRIX_V unity_MatrixV
#define UNITY_MATRIX_I_V unity_MatrixInvV
#define UNITY_MATRIX_VP unity_MatrixVP
#define UNITY_PREV_MATRIX_M unity_prev_MatrixM
#define UNITY_PREV_MATRIX_I_M unity_prev_MatrixIM
#define UNITY_MATRIX_P glstate_matrix_projection#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/SpaceTransforms.hlsl"float Square(float x) {return x * x;
}#endif

 

ShaderLibrary/Surface.hlsl

#ifndef CUSTOM_SURFACE_INCLUDED
#define CUSTOM_SURFACE_INCLUDED//光照射到的物体表面的相关信息
struct Surface {float3 normal; //法线float3 viewDirection; //视角方向float3 color; //光照计算前的颜色float alpha;float metallic; //金属度float smoothness; //光滑度
};#endif

 

ShaderLibrary/Light.hlsl

#ifndef CUSTOM_LIGHT_INCLUDED
#define CUSTOM_LIGHT_INCLUDED#define MAX_DIRECTIONAL_LIGHT_COUNT 4CBUFFER_START(_CustomLight)int _DirectionalLightCount;float4 _DirectionalLightColors[MAX_DIRECTIONAL_LIGHT_COUNT];float4 _DirectionalLightDirections[MAX_DIRECTIONAL_LIGHT_COUNT];
CBUFFER_ENDstruct Light {float3 color; //光源颜色float3 direction; //光源方向
};//平行光总数量
int GetDirectionalLightCount() {return _DirectionalLightCount;
}//获取某个平行光信息
Light GetDirectionalLight(int index) {Light light;light.color = _DirectionalLightColors[index].rgb;light.direction = _DirectionalLightDirections[index].xyz;return light;
}#endif

 

ShaderLibrary/BRDF.hlsl

#ifndef CUSTOM_BRDF_INCLUDED
#define CUSTOM_BRDF_INCLUDED//光照到物体表面后, 一部分被漫反射(向各个方向均匀反射), 其余部分被镜面反射(某个方向完全反射)。 注意: 折射和进入物体等情况不考虑时
//金属度决定镜面反射比例, 光滑度决定镜面反射比例中有多少比例反射出来//双向反射分布计算要用到的数据
struct BRDF {float3 diffuse;  //漫反射float3 specular; //高光float roughness; //粗糙度
};#define MIN_REFLECTIVITY 0.04 //非金属的平均镜面反射比例//获取漫反射比例
float OneMinusReflectivity(float metallic) {float range = 1.0 - MIN_REFLECTIVITY;return range - metallic * range; //确保metallic=0时也有镜面反射
}//双向反射分布计算后的漫反射(会比物体原有颜色弱)和镜面反射
BRDF GetBRDF(Surface surface, bool applyAlphaToDiffuse = false) {BRDF brdf;float oneMinusReflectivity = OneMinusReflectivity(surface.metallic);brdf.diffuse = surface.color * oneMinusReflectivity;if (applyAlphaToDiffuse) {brdf.diffuse *= surface.alpha;}brdf.specular = lerp(MIN_REFLECTIVITY, surface.color, surface.metallic);//迪士尼光照模型中的方法计算出粗糙度float perceptualRoughness = PerceptualSmoothnessToPerceptualRoughness(surface.smoothness);brdf.roughness = PerceptualRoughnessToRoughness(perceptualRoughness); //直观方式表示的粗糙度(即: 0~1)转换为公式使用的粗糙度return brdf;
}//高光反射强度取决于视角方向和完全发射方向的正对程度, 这边使用简化版CookTorrance BRDF公式计算
float SpecularStrength(Surface surface, BRDF brdf, Light light) {// r^2 / (d^2*max(0.1, (L·H)^2)*n), r为粗糙度// d = (N·H)^2*(r^2 - 1) + 1.0001, N为表面法线方向// H = L+V, L为光源方向, V为视角方向// n = 4*r+2float3 h = SafeNormalize(light.direction + surface.viewDirection);float nh2 = Square(saturate(dot(surface.normal, h)));float lh2 = Square(saturate(dot(light.direction, h)));float r2 = Square(brdf.roughness);float d2 = Square(nh2 * (r2 - 1.0) + 1.00001);float normalization = brdf.roughness * 4.0 + 2.0;return r2 / (d2 * max(0.1, lh2) * normalization);
}//平行光的BRDF
float3 DirectBRDF(Surface surface, BRDF brdf, Light light) {//高光反射叠加漫反射(叠加用+,混合用乘)return SpecularStrength(surface, brdf, light) * brdf.specular + brdf.diffuse;
}#endif

 

ShaderLibrary/Lighting.hlsl

#ifndef CUSTOM_LIGHTING_INCLUDED
#define CUSTOM_LIGHTING_INCLUDED//入射光强度计算
float3 IncomingLight(Surface surface, Light light) {return saturate(dot(surface.normal, light.direction)) * light.color;
}//单个入射光照到物体表面后的光照的计算
float3 GetLighting(Surface surface, BRDF brdf, Light light) {return IncomingLight(surface, light) * DirectBRDF(surface, brdf, light);
}//所有光照的计算
float3 GetLighting(Surface surface, BRDF brdf) {float3 color = 0.0;for (int i = 0; i < GetDirectionalLightCount(); i++) {color += GetLighting(surface, brdf, GetDirectionalLight(i));}return color;
}#endif

 

LitPass.hlsl

#ifndef CUSTOM_LIT_PASS_INCLUDED
#define CUSTOM_LIT_PASS_INCLUDED#include "../ShaderLibrary/Common.hlsl"
#include "../ShaderLibrary/Surface.hlsl"
#include "../ShaderLibrary/Light.hlsl"
#include "../ShaderLibrary/BRDF.hlsl"
#include "../ShaderLibrary/Lighting.hlsl"TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)UNITY_DEFINE_INSTANCED_PROP(float4, _BaseMap_ST)UNITY_DEFINE_INSTANCED_PROP(float4, _BaseColor)UNITY_DEFINE_INSTANCED_PROP(float, _Cutoff)UNITY_DEFINE_INSTANCED_PROP(float, _Metallic)UNITY_DEFINE_INSTANCED_PROP(float, _Smoothness)
UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)struct Attributes {  //程序传入顶点着色器的数据float3 positionOS : POSITION; //用模型空间顶点坐标填充该变量float3 normalOS : NORMAL; //用模型空间法线方向填充该变量float2 baseUV : TEXCOORD0; //用模型的第一套纹理坐标(uv)填充该变量
    UNITY_VERTEX_INPUT_INSTANCE_ID
};struct Varyings { //顶点着色器传入片元着色器的数据float4 positionCS : SV_POSITION; //该变量存放了裁剪空间的顶点坐标
    float3 positionWS : VAR_POSITION;float3 normalWS : VAR_NORMAL;float2 baseUV : VAR_BASE_UV;UNITY_VERTEX_INPUT_INSTANCE_ID
};Varyings LitPassVertex (Attributes input) { //顶点着色器
    Varyings output;UNITY_SETUP_INSTANCE_ID(input);UNITY_TRANSFER_INSTANCE_ID(input, output);output.positionWS = TransformObjectToWorld(input.positionOS); //模型空间转世界空间output.positionCS = TransformWorldToHClip(output.positionWS); //世界空间转裁剪空间output.normalWS = TransformObjectToWorldNormal(input.normalOS);float4 baseST = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseMap_ST);output.baseUV = input.baseUV * baseST.xy + baseST.zw; //应用贴图的tiling和offsetreturn output;
}float4 LitPassFragment (Varyings input) : SV_TARGET { //片元着色器
    UNITY_SETUP_INSTANCE_ID(input);float4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.baseUV); //根据纹理坐标获取像素颜色float4 baseColor = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseColor);float4 base = baseMap * baseColor;#if defined(_CLIPPING)clip(base.a - UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Cutoff));#endifSurface surface;surface.normal = normalize(input.normalWS);surface.viewDirection = normalize(_WorldSpaceCameraPos - input.positionWS); //视角方向surface.color = base.rgb;surface.alpha = base.a;surface.metallic = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Metallic);surface.smoothness =UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Smoothness);#if defined(_PREMULTIPLY_ALPHA)BRDF brdf = GetBRDF(surface, true);#elseBRDF brdf = GetBRDF(surface);#endiffloat3 color = GetLighting(surface, brdf);return float4(color, surface.alpha);
}#endif

 

Lit.shader

Shader "Custom RP/Lit" {Properties {_BaseMap("Texture", 2D) = "white" {} //主贴图_BaseColor("Color", Color) = (0.5, 0.5, 0.5, 1.0) //混合颜色_Cutoff ("Alpha Cutoff", Range(0.0, 1.0)) = 0.5[Toggle(_CLIPPING)] _Clipping ("Alpha Clipping", Float) = 0_Metallic ("Metallic", Range(0, 1)) = 0  //金属度_Smoothness ("Smoothness", Range(0, 1)) = 0.5  //光滑度
[Toggle(_PREMULTIPLY_ALPHA)] _PremulAlpha ("Premultiply Alpha", Float) = 0[Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend ("Src Blend", Float) = 1[Enum(UnityEngine.Rendering.BlendMode)] _DstBlend ("Dst Blend", Float) = 0[Enum(Off, 0, On, 1)] _ZWrite ("Z Write", Float) = 1}SubShader {Pass {Tags {"LightMode" = "CustomLit"}Blend [_SrcBlend] [_DstBlend]ZWrite [_ZWrite]HLSLPROGRAM //hlsl代码需要用这个包着#pragma target 3.5 //Shader Model版本低于3.5, 核心库无法通过编译#pragma shader_feature _CLIPPING#pragma shader_feature _PREMULTIPLY_ALPHA#pragma multi_compile_instancing#pragma vertex LitPassVertex#pragma fragment LitPassFragment#include "LitPass.hlsl"ENDHLSL}}
}

 

翻译备忘:

We would only see this light if the camera were aligned with it
只有当相机正对它时,我们才能看到这束光
 
But if the surface isn't perfectly flat then the light gets scattered
如果表面不是完全光滑的, 那么光就会散射
 
because the fragment effectively consists of many smaller fragments that have different orientations
因为片元实际上由许多不同朝向的小片元组成
 
This splits the beam of light into smaller beams that go in different directions, which effectively blurs the specular reflection
这将一束光分散成更小的光(不同方向),这有效的柔化了镜面反射
 
Surfaces can be perfectly diffuse, perfect mirrors, or anything in between
表面可以完全漫反射, 完全镜面反射, 或介于两者之间

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uart串口的低速通信基础知识及模块代码(来自正点原子P15)

正点原子P15在PL端的uart电路参考,PS端uart和PL端一致,这里不做重复,uart电路由电脑端进行供电,即uart和主芯片之间除利用uart_tx和uart_rx通信外是独立的。从上图中可以看到,FPGA芯片的PL_UART1_TX连接到CH340的RXD管脚,FPGA芯片的PL_UART1_RX连接到 CH340 的 TXD 管脚,…

I/O框架

流的概念、流的分类、字节流、字符编码、字符流、打印流、转换流和File类。流的概念概念:内存与存储设备之间传输数据的通道。流的分类按方向【重点】输入流:将<存储设备>中的内容读入到<内存>中。 输出流:将<内存>中的内容写入到<存储设备>中。按单…

网站向顾客发送电子邮件

首先说一下,针对顾客未登录就可下单这个功能,为了使用户可以实时知晓货品的物流状态,使用了advance shipment tracking这个插件,这个插件不仅可以显示货品的物流信息,还可以在货品物流状态更新时向顾客发送电子邮件,这样就实现了顾客在未登录时就可以知道自己购买的商品的…

2025.1.20——1300

2025.1.20——1300A 1300 You are given a binary string \(s\). A binary string is a string consisting of characters 0 and/or 1. You can perform the following operation on \(s\) any number of times (even zero):choose an integer \(i\) such that \(1 \le i \le |…

制作docker 镜像上传到docker hub仓库

注册docker hub账号 https://hub.docker.com/ 参照此篇:https://www.cnblogs.com/yjlch1016/p/8998479.htmldocker hub上创建仓库https://hub.docker.com/repositories 本地制作镜像并上传在本地登陆 docker hub 帐号docker login将容器commit 成镜像,可以先用docker …

虚拟现实国标解读系列(一)帧率

大家好,我是ij(我的网名),中文名叫林志宏。 遵循我一贯的年底必摸鱼的习惯,我打算开始摸鱼来水一些文章,安慰下自己过去的一年。 有关注过我,听我吹过牛的都知道,我在几年前,也不知道几年前, 反正long long ago,我参与起草过一份牛逼的测试标准,国标GB/T 38258《虚…

【Linux网络】深入理解linux内核网络性能优化

一、网络请求优化 1.1 减少不必要的网络IO 在系统设计与开发过程中,应尽量避免不必要的网络I/O操作,尤其是在可以通过本地进程或内存内完成的场景下,避免使用网络通信来实现。网络虽然是现代分布式系统中的核心组件,能够连接不同模块、简化开发流程,并支持大规模系统的构建…

【Java开发】简化Maven项目依赖:优雅去除未使用Jar包

一、为什么要做这件事? 自从我踏入职场,便历经了技术革新的数次浪潮。从最初的.Net Framework、Winform、WPF,到Asp.Net MVC、Asp.Net MVC WebApi,再到Asp.Net Core 2.x的广泛应用,我始终深耕于.net领域。 然而,随着技术的不断演进,我逐渐发现.net相关的工作机会变得稀少…

《操作系统真相还原》实验记录2.7——生产者与消费者问题

本节实现内容如下: ① 环形缓存区的结构体创建; ② 环形键盘缓冲区的创建; ③ 生产者消费者问题剖析;一、生产者与消费者问题简述我们知道,在计算机中可以并行多个线程,当它们之间相互合作时,必然会存在共享资源的问题,这是通过“线程同步”来解决的,而诠释“线程同步…

CTF-web第一步!

本次的题比较简单,适合我这种入门学者。CTF菜狗杯的web2 c0me_t0_s1gn。进入靶场打开F12会得到前一半。在控制台复制函数give_flag()会得到另一半。 这样就完成了。

P1183 多边形的面积-向量的用法

原题链接 https://www.luogu.com.cn/problem/P1183 题目描述 给出一个没有缺口的简单多边形,它的边是垂直或者水平的,要求计算多边形的面积。 xOy 的笛卡尔平面上,它所有的边都平行于两条坐标轴之一。然后按逆时针方向给出各顶点的坐标值。所有的坐标值都是整数,因此多边形…

蓄水池漂浮物识别摄像机

蓄水池漂浮物识别摄像机具有高效的图像识别功能。通过高清晰度的摄像头捕捉到蓄水池表面的图像,并通过人工智能技术进行快速准确的漂浮物识别。这种摄像机可以自动检测出池面上的漂浮物,并生成相应报警信息。该摄像机支持多种智能算法分析,并通过智能算法对数据进行综合分析…