Unity SRP 管线【第四讲:URP 阴影】

URP 全文源码解析参照

引入

在UniversalRenderer.cs/ line 505行处
此处已经准备好了所有渲染数据(所有数据全部存储在了renderingData中)
我们只用renderingData中的数据初设置mainLightShadows

bool mainLightShadows = m_MainLightShadowCasterPass.Setup(ref renderingData);
bool additionalLightShadows = m_AdditionalLightsShadowCasterPass.Setup(ref renderingData);
bool transparentsNeedSettingsPass = m_TransparentSettingsPass.Setup(ref renderingData);

进入函数 m_MainLightShadowCasterPass.Setup(ref renderingData);

在这里插入图片描述

文章目录

  • URP 全文源码解析参照
  • 引入
  • MainLightShadowCasterPass.cs
    • 一、MainLightShadowCasterPass 创建
      • 参数:
        • 基类参数
        • 自定义参数
        • GPU ID参数
    • 二、MainLightShadowCasterPass 初始化
      • 1. 初始化数据(矩阵、cascade分割、切片)
      • 2. 一些条件判断,未通过的话将ShadowMap置为默认值
      • 3. 设置数据
            • (1)m_ShadowCasterCascadesCount
            • (2)RenderTexture 大小
            • (3)提取级联阴影的光照矩阵以及包围球
            • (4)获取新的临时阴影纹理
            • (5)存储renderingData中的阴影数据
      • 4. 绑定渲染目标和清除状态
      • 5. 执行 Execute
    • 三、Execute 阴影参数设置
  • GPU中的阴影数据

MainLightShadowCasterPass.cs

一、MainLightShadowCasterPass 创建

public MainLightShadowCasterPass(RenderPassEvent evt)

在UniversalRenderer管线创建的时候,我们已经做了创建

我们设置MainLightShadow在RenderingShadows(Event = 50)之前

// UniversalRenderer.cs
public UniversalRenderer(UniversalRendererData data) : base(data)
{
	...
	m_MainLightShadowCasterPass = new MainLightShadowCasterPass(RenderPassEvent.BeforeRenderingShadows);
	m_AdditionalLightsShadowCasterPass = new AdditionalLightsShadowCasterPass(RenderPassEvent.BeforeRenderingShadows);
	...
}

创建MainLightShadowCasterPass

参数:

基类参数
public RenderPassEvent renderPassEvent { get; set; }
int renderTargetWidth { get; set; }
int renderTargetHeight { get; set; }
自定义参数
const int k_MaxCascades = 4;
const int k_ShadowmapBufferBits = 16;
float m_CascadeBorder;                  // 边缘模糊度(0~1)
float m_MaxShadowDistanceSq;			// 最大阴影距离的平方
int m_ShadowCasterCascadesCount;		// 当前设置的Cascades数量

RenderTargetHandle m_MainLightShadowmap;           //临时阴影纹理Shader中的索引ID
internal RenderTexture m_MainLightShadowmapTexture;//实际临时阴影纹理,以及设置(深度为16位)

Matrix4x4[] m_MainLightShadowMatrices;// 阴影矩阵
ShadowSliceData[] m_CascadeSlices;    // 级联阴影切片数据,包含(
						//splitData、       级联阴影数据(由UNITY内置函数提供)
						//offsetX/Y、       级联阴影偏移
						//resolution        级联阴影分辨率
						//shadowTransform   级联阴影矩阵
Vector4[] m_CascadeSplitDistances;    // 包围球数据(xyz:位置,w:半径)
GPU ID参数
private static class MainLightShadowConstantBuffer
{
    public static int _WorldToShadow;
    public static int _ShadowParams;
    public static int _CascadeShadowSplitSpheres0;
    public static int _CascadeShadowSplitSpheres1;
    public static int _CascadeShadowSplitSpheres2;
    public static int _CascadeShadowSplitSpheres3;
    public static int _CascadeShadowSplitSphereRadii;
    public static int _ShadowOffset0;
    public static int _ShadowOffset1;
    public static int _ShadowOffset2;
    public static int _ShadowOffset3;
    public static int _ShadowmapSize;
}
MainLightShadowConstantBuffer._WorldToShadow = Shader.PropertyToID("_MainLightWorldToShadow");
MainLightShadowConstantBuffer._ShadowParams = Shader.PropertyToID("_MainLightShadowParams");
MainLightShadowConstantBuffer._CascadeShadowSplitSpheres0 = Shader.PropertyToID("_CascadeShadowSplitSpheres0");
MainLightShadowConstantBuffer._CascadeShadowSplitSpheres1 = Shader.PropertyToID("_CascadeShadowSplitSpheres1");
MainLightShadowConstantBuffer._CascadeShadowSplitSpheres2 = Shader.PropertyToID("_CascadeShadowSplitSpheres2");
MainLightShadowConstantBuffer._CascadeShadowSplitSpheres3 = Shader.PropertyToID("_CascadeShadowSplitSpheres3");
MainLightShadowConstantBuffer._CascadeShadowSplitSphereRadii = Shader.PropertyToID("_CascadeShadowSplitSphereRadii");
MainLightShadowConstantBuffer._ShadowOffset0 = Shader.PropertyToID("_MainLightShadowOffset0");
MainLightShadowConstantBuffer._ShadowOffset1 = Shader.PropertyToID("_MainLightShadowOffset1");
MainLightShadowConstantBuffer._ShadowOffset2 = Shader.PropertyToID("_MainLightShadowOffset2");
MainLightShadowConstantBuffer._ShadowOffset3 = Shader.PropertyToID("_MainLightShadowOffset3");
MainLightShadowConstantBuffer._ShadowmapSize = Shader.PropertyToID("_MainLightShadowmapSize");
m_MainLightShadowmap.Init("_MainLightShadowmapTexture");

二、MainLightShadowCasterPass 初始化

public bool Setup(ref RenderingData renderingData){}

1. 初始化数据(矩阵、cascade分割、切片)

Clear();

2. 一些条件判断,未通过的话将ShadowMap置为默认值

… 这里不作为重点

3. 设置数据

SetUp函数主要是将数据整理保存到类内参数中供类使用
其中设置的数据有:

(1)m_ShadowCasterCascadesCount

光源级联阴影数量

(2)RenderTexture 大小
renderTargetWidth;
renderTargetHeight;
(3)提取级联阴影的光照矩阵以及包围球
  1. Vector4[] m_CascadeSplitDistances[cascadeIndex] 包围球数据(xyz:位置,w:半径)
  2. ShadowSliceData[] m_CascadeSlices; 级联阴影切片数据
public struct ShadowSliceData
{
    public Matrix4x4 viewMatrix;
    public Matrix4x4 projectionMatrix;
    public Matrix4x4 shadowTransform;//projectionMatrix * viewMatrix
    public int offsetX;			     //ShadowAtlasMap行偏移
    public int offsetY;				 //ShadowAtlasMap列偏移
    public int resolution;			 //分辨率
    public ShadowSplitData splitData;//splitData包含筛选信息
}

提取函数

for (int cascadeIndex = 0; cascadeIndex < m_ShadowCasterCascadesCount; ++cascadeIndex)
{
    bool success = ShadowUtils.ExtractDirectionalLightMatrix(
    	ref renderingData.cullResults, 
    	ref renderingData.shadowData,
        shadowLightIndex, 
        cascadeIndex, 
        renderTargetWidth, 
        renderTargetHeight, 
        shadowResolution, 
        light.shadowNearPlane,
        out m_CascadeSplitDistances[cascadeIndex], 
        out m_CascadeSlices[cascadeIndex]);

    if (!success)
        return SetupForEmptyRendering(ref renderingData);
}
(4)获取新的临时阴影纹理
m_MainLightShadowmapTexture = ShadowUtils.GetTemporaryShadowTexture(renderTargetWidth, renderTargetHeight, k_ShadowmapBufferBits);
(5)存储renderingData中的阴影数据
m_MaxShadowDistanceSq = renderingData.cameraData.maxShadowDistance * renderingData.cameraData.maxShadowDistance;
m_CascadeBorder = renderingData.shadowData.mainLightShadowCascadeBorder;

4. 绑定渲染目标和清除状态

public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
{
    ConfigureTarget(new RenderTargetIdentifier(m_MainLightShadowmapTexture), m_MainLightShadowmapTexture.depthStencilFormat, renderTargetWidth, renderTargetHeight, 1, true);
    ConfigureClear(ClearFlag.All, Color.black);
}

5. 执行 Execute

public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
    if (m_CreateEmptyShadowmap)
    {
        SetEmptyMainLightCascadeShadowmap(ref context);
        return;
    }

    RenderMainLightCascadeShadowmap(ref context, ref renderingData.cullResults, ref renderingData.lightData, ref renderingData.shadowData);
}

三、Execute 阴影参数设置

RenderMainLightCascadeShadowmap(
			ref context, 
			ref renderingData.cullResults, 
			ref renderingData.lightData, 
			ref renderingData.shadowData);

参数设置多引用ShadowsUtils单例中的函数设置
进入函数RenderMainLightCascadeShadowmap

// settings 中为阴影所需要的数据
var settings = new ShadowDrawingSettings(cullResults, shadowLightIndex);
settings.useRenderingLayerMaskTest = UniversalRenderPipeline.asset.supportsLightLayers;

for (int cascadeIndex = 0; cascadeIndex < m_ShadowCasterCascadesCount; ++cascadeIndex)
{
    settings.splitData = m_CascadeSlices[cascadeIndex].splitData;

    // 偏移量
    Vector4 shadowBias = ShadowUtils.GetShadowBias(ref shadowLight, shadowLightIndex, ref shadowData, m_CascadeSlices[cascadeIndex].projectionMatrix, m_CascadeSlices[cascadeIndex].resolution);
    ShadowUtils.SetupShadowCasterConstantBuffer(cmd, ref shadowLight, shadowBias);
    
    CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.CastingPunctualLightShadow, false);
    ShadowUtils.RenderShadowSlice(cmd, ref context, ref m_CascadeSlices[cascadeIndex],
        ref settings, m_CascadeSlices[cascadeIndex].projectionMatrix, m_CascadeSlices[cascadeIndex].viewMatrix);
}

shadowData.isKeywordSoftShadowsEnabled = shadowLight.light.shadows == LightShadows.Soft && shadowData.supportsSoftShadows;
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MainLightShadows, shadowData.mainLightShadowCascadesCount == 1);
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MainLightShadowCascades, shadowData.mainLightShadowCascadesCount > 1);
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.SoftShadows, shadowData.isKeywordSoftShadowsEnabled);

SetupMainLightShadowReceiverConstants(cmd, shadowLight, shadowData.supportsSoftShadows);
  1. SetupShadowCasterConstantBuffer
// 阴影偏移
cmd.SetGlobalVector("_ShadowBias", shadowBias);

// 世界空间光照方向,用于支持阴影法线偏移(normal bias).
Vector3 lightDirection = -shadowLight.localToWorldMatrix.GetColumn(2);
cmd.SetGlobalVector("_LightDirection", new Vector4(lightDirection.x, lightDirection.y, lightDirection.z, 0.0f));

// 世界空间光源位置
Vector3 lightPosition = shadowLight.localToWorldMatrix.GetColumn(3);
cmd.SetGlobalVector("_LightPosition", new Vector4(lightPosition.x, lightPosition.y, lightPosition.z, 1.0f));
  1. 关闭精确光源阴影 PunctualLightShadow(点光源、方向光、聚光灯)
/*  "_CASTING_PUNCTUAL_LIGHT_SHADOW"  */
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.CastingPunctualLightShadow, false);
  1. 渲染cascade阴影
    设置默认全局偏移(这应该就是为什么,在unity中,将light的bias都设为0,但还是没有明显的摩尔纹)
cmd.SetGlobalDepthBias(1.0f, 2.5f); // these values match HDRP defaults 

设置Viewport、矩阵信息

cmd.SetViewport(new Rect(shadowSliceData.offsetX, shadowSliceData.offsetY, shadowSliceData.resolution, shadowSliceData.resolution));
cmd.SetViewProjectionMatrices(view, proj);

渲染阴影,禁用硬件剪刀矩阵(不知道是什么)

context.DrawShadows(ref settings);
cmd.DisableScissorRect();
  1. 是否开启主光源阴影、主光源级联阴影、主光源软阴影
/*"_MAIN_LIGHT_SHADOWS"*/
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MainLightShadows, shadowData.mainLightShadowCascadesCount == 1);
/*"_MAIN_LIGHT_SHADOWS_CASCADE"*/
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.MainLightShadowCascades, shadowData.mainLightShadowCascadesCount > 1);
/*"_SHADOWS_SOFT"*/
CoreUtils.SetKeyword(cmd, ShaderKeywordStrings.SoftShadows, shadowData.isKeywordSoftShadowsEnabled);
  1. 设置阴影接收常量

    1. 阴影贴图 “_MainLightShadowmapTexture
    2. 光源矩阵 “_MainLightWorldToShadow”(级联矩阵为多个矩阵 Matrix4x4[])
    3. 阴影参数 “_MainLightShadowParams
      • x :阴影强度
      • y :是否软阴影(1为软,0为硬)
      • z :阴影衰减部分的衰减斜率
      • w:横坐标0处阴影开始衰减部分的纵坐标
    4. 级联阴影数据:
      四个级联的包围球数据(4 * Vector4) _CascadeShadowSplitSpheres0、1、2、3
      四个级联的半径的平方(1 * Vector4) _CascadeShadowSplitSphereRadii
    5. 软阴影:
      阴影像素偏移(左下、右下、左上、右上) _MainLightShadowOffset0/1/2/3
      阴影偏移量(偏移x,偏移y,shadowMap宽,shadowMap长 _MainLightShadowmapSize
  2. 在渲染结束后释放临时Texture

public override void OnCameraCleanup(CommandBuffer cmd)
{
    if (cmd == null)
        throw new ArgumentNullException("cmd");

    if (m_MainLightShadowmapTexture)
    {
        RenderTexture.ReleaseTemporary(m_MainLightShadowmapTexture);
        m_MainLightShadowmapTexture = null;
    }
}

GPU中的阴影数据

Shadows.hlsl

SCREENSPACE_TEXTURE(_ScreenSpaceShadowmapTexture);
SAMPLER(sampler_ScreenSpaceShadowmapTexture);

TEXTURE2D_SHADOW(_MainLightShadowmapTexture);
SAMPLER_CMP(sampler_MainLightShadowmapTexture);

TEXTURE2D_SHADOW(_AdditionalLightsShadowmapTexture);
SAMPLER_CMP(sampler_AdditionalLightsShadowmapTexture);

CBUFFER_START(MainLightShadows)
	// Last cascade is initialized with a no-op matrix. It always transforms
	// shadow coord to half3(0, 0, NEAR_PLANE). We use this trick to avoid
	// branching since ComputeCascadeIndex can return cascade index = MAX_SHADOW_CASCADES
	float4x4    _MainLightWorldToShadow[MAX_SHADOW_CASCADES + 1];
	float4      _CascadeShadowSplitSpheres0;
	float4      _CascadeShadowSplitSpheres1;
	float4      _CascadeShadowSplitSpheres2;
	float4      _CascadeShadowSplitSpheres3;
	float4      _CascadeShadowSplitSphereRadii;
	half4       _MainLightShadowOffset0;
	half4       _MainLightShadowOffset1;
	half4       _MainLightShadowOffset2;
	half4       _MainLightShadowOffset3;
	half4       _MainLightShadowParams;   // (x: shadowStrength, y: 1.0 if soft shadows, 0.0 otherwise, z: main light fade scale, w: main light fade bias)
	float4      _MainLightShadowmapSize;  // (xy: 1/width and 1/height, zw: width and height)
CBUFFER_END

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

德人合科技 | 防止公司电脑文件数据资料外泄,自动智能透明加密保护系统

【透明加密软件】——防止公司电脑文件数据资料防止外泄&#xff0c;自动智能透明加密保护内部核心文件、文档、图纸、源代码、音视频等资料&#xff01; PC端访问地址&#xff1a; www.drhchina.com &#x1f31f; 核心功能&#xff1a; 透明加密&#xff1a;采用高级加密算…

VR虚拟动漫角色智能化导览丰富体验乐趣

AI数字人助理可以为我们带来哪些帮助? 随着人工智能技术的不断发展&#xff0c;AI数字人助理已经成为了我们日常生活和工作中的得力助手。它们具备智能感知、语音识别、自然语言处理等多种技能&#xff0c;可以为我们带来很多帮助和便利。 一、提高工作效率 AI数字人助理可以帮…

SCC-Tarjan,缩点问题

文章目录 前言引例什么是缩点&#xff1f;缩点的应用一、合并强连通子图为强连通图题目描述输入/输出格式原题链接题目详解 二、集合间偏序关系题目描述输入/输出格式原题链接题目详解 三、最大点权和路径题目描述输入/输出格式原题链接题目详解 其他OJ练习 前言 图论中的缩点问…

【MySQL·8.0·源码】MySQL 语法树基础知识

基础 我们都知道 SQL 语句经过词法分析器时&#xff0c;识别扫描输入的 SQL 语句&#xff0c;将关键词、标识符、常量等分解转换成独立的 tokens&#xff0c;进一步在语法分析阶段根据语法规则检查 tokens 序列的结构并不断 shift 、reduce 构建成 SQL 语法解析树。 在 MySQL…

ubuntu18.04 64 位安装笔记——备赛笔记——2024全国职业院校技能大赛“大数据应用开发”赛项——任务2:离线数据处理

进入VirtuakBox官网&#xff0c;网址链接&#xff1a;Oracle VM VirtualBoxhttps://www.virtualbox.org/ 网页连接&#xff1a;Ubuntu Virtual Machine Images for VirtualBox and VMwarehttps://www.osboxes.org/ubuntu/ 将下发的ds_db01.sql数据库文件放置mysql中 12、编写S…

19 高速列车场景下3Gpp 5G NR的DMRS设计与评估

文章目录 解决问题设计DMRS仿真参数仿真结果 解决问题 多普勒/扩展影响十分显著&#xff0c;设计用于信道估计时&#xff0c;需要考虑解调参考信号&#xff0c;5G用DMRS结构而不是CRS结构&#xff0c;因此需要为高速UE设计DMRS结构&#xff0c;DMRS设计是为了提高信道估计并减…

jdk多版本切换环境变量管理(jdk1.8和jdk17)

jdk多版本切换环境变量管理&#xff08;jdk1.8和jdk17&#xff09; 看了很多网上的博客&#xff0c;根本都不行&#xff0c;我总结出来规律如下&#xff1a; 首先环境变量要配置成这个样子&#xff1a;这些博客都会教你们配 接着配什么classpath&#xff0c;看其他博客就行 还…

百度地图添加坐标点,并返回坐标信息

1、创建地图容器 在mounted中初始化地图、鼠标绘制工具和添加鼠标监听事件 vue data中添加地图和绘制工具对象 2、添加初始化化地图方法 initMap(longitude, latitude) {let that thisthat.map new BMapGL.Map("container");// 创建地图实例if (longitude null ||…

亿某通电子文档安全管理系统任意文件上传漏洞 CNVD-2023-59471

1.漏洞概述 亿某通电子文档安全管理系统是一款电子文档安全防护软件,该系统利用驱动层透明加密技术,通过对电子文档的加密保护,防止内部员工泄密和外部人员非法窃取企业核心重要数据资产。亿赛通电子文档安全管理系统UploadFileFromClientServiceForClient接口处存在任意文件…

大创项目推荐 深度学习 opencv python 公式识别(图像识别 机器视觉)

文章目录 0 前言1 课题说明2 效果展示3 具体实现4 关键代码实现5 算法综合效果6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的数学公式识别算法实现 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学…

2024年【建筑电工(建筑特殊工种)】报名考试及建筑电工(建筑特殊工种)新版试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年建筑电工(建筑特殊工种)报名考试为正在备考建筑电工(建筑特殊工种)操作证的学员准备的理论考试专题&#xff0c;每个月更新的建筑电工(建筑特殊工种)新版试题祝您顺利通过建筑电工(建筑特殊工种)考试。 1、【单…

内网离线搭建之----nginx高可用

1.系统版本 虚拟机192.168.9.184 虚拟机192.168.9.185 2.nginx以及依赖下载地址 nginx&#xff1a;nginx: download pcre&#xff1a;PCRE - Browse /pcre/8.45 at SourceForge.net zlib&#xff1a;zlib Home Site 基本都在置顶的资源里 3.检查环境安装依赖的依赖&#xf…

RS232转profinet网关扫码枪自由口与1500程序对比

RS232转profinet网关&#xff08;XD-PNR200&#xff09;自由口是一种用于将RS232串口信号转换为profinet协议的设备&#xff0c;它具有自由口的功能。本文以某自动化生产线为例进行案例研究。通过RS232转Profinet网关&#xff08;XD-PNR200&#xff09;&#xff0c;将生产线的多…

Elasticsearch 【版本7.X】之常用的语法大全

文章目录 监控相关 API 查看健康状况查看所有节点查看所有节点详细信息查看主节点查看所有索引查看所有分片 索引管理 创建索引查看索引查看索引字段类型修改索引字段删除索引别名 给索引添加别名查询某个索引下的别名给索引更换别名给索引解绑别名一个别名绑定多个索引查询ind…

xv6 文件系统(上)

〇、前言 本文将会结合 xv6 源码讨论文件系统的工作原理。 一、文件系统实现概述 xv6 文件系统可以用下面的图来表示&#xff1a; 按照分层的方式进行理解&#xff1a; 在最底层是磁盘&#xff0c;也就是一些实际保存数据的存储设备&#xff0c;正是这些设备提供了持久化存…

mysql函数()之常见字符串函数

MySQL中常见的字符串函数有以下几种&#xff1a; CONCAT()&#xff1a;将两个或多个字符串连接在一起。 用法&#xff1a;CONCAT(string1, string2, …) 效果图&#xff1a; LENGTH()&#xff1a;返回字符串的长度。 用法&#xff1a;LENGTH(string) 效果图&#xff1a; UP…

7款不容错过的前端特效源码分享(附源码及演示效果)

分享7款非常不错炫酷的前端特效源码 其中包含css动画特效、js原生特效、svg特效等 下面我会列出核心的代码 但你也可以点击预览获取查看该源码的最终展示效果及下载该源码资源 图像网格动画 纯js和css实现的多方位多样式的图像网格动画 点击预览获取 核心代码 <div class…

基于JSP+Servlet+JavaBean+JDBC+DAO的Web架构设计图书管理系统

系统实现如下的基本管理功能: (1)用户分为两类:系统管理员&#xff0c;一般用户。 (2)提供用户注册和用户登录验证功能:其中一个登录用户的信息有:登录用户名&#xff0c;登录密码。 (3)管理员可以实现对注册用户的管理(删除)&#xff0c;并实现对图书的创建、查询、修改和删…

Java算法(十二):【数据结构与算法】 十大排序 之 二分查法 二分查法实现详细流程图分析 实现源码实例

二分查找 二分查找 二分查找就是返回有序序列中&#xff0c;需要查找的元素索引&#xff0c;无则-1。 需求&#xff1a;二分查找&#xff1a;手写实现数组元素的查找&#xff0c;存在返回索引&#xff0c;无则返回 -1&#xff1b;实现思路&#xff1a;&#xff08;前提是有序…

“源味河南县 牛羊天下鲜”河南县有机农畜产品发布会在博鳌召开

2023年12月17日&#xff0c;由中共青海省黄南州河南蒙古族自治县委员会、县人民政府主办的“源味河南县 牛羊天下鲜”绿色有机农畜产品发布会在海南博鳌举办。原农业部党组成员、中国农产品市场协会会长张玉香&#xff0c;原农业部党组成员、中国奶业协会战略发展委员会名誉副主…