【DirectX11】第十篇 其他灯光类型——点光源

本系列文章主要翻译和参考自《Real-Time 3D Rendering with DirectX and HLSL》一书(感谢原书作者),同时会加上一点个人理解和拓展,文章中如有错误,欢迎指正。

这里是书中的代码和资源。

本文所有的环境和工具使用都基于之前的文章,如有不明白的地方请先参考本系列之前的几篇文章。

本文索引:

  • 关于灯光类型
  • Point Light 什么是点光源
    • 1 Point Light Preamble 点光源变量准备
    • 2 Point Light Vertex and Pixel Shader 点光源顶点及像素着色器的实现
    • 3 Point Light Output 点光源输出效果
    • 4 Point Light Modifications 修改点光源的实现

关于灯光类型

在前面的介绍光照模型的几篇文章中,主要为大家介绍了一些很基本的光照模型是如何实现的。文章中提到的计算方式都是在假定环境中只存在平行光的情况下计算的。之前我们有介绍过,灯光有很多种类型,包括点光源、平行光、聚光灯等……。下面将为大家介绍除平行光之外的这几种灯光。

Point Light: 什么是点光源

点光源的效果类似电灯泡,会向所有方向均匀地放出光,在场景中的点光源是由具体位置的区别的。这些就和平行光不一样,平行光没有具体位置的区别,他是从无限远的地方照射过来的光,并且是有一个统一的光线方向的。总的来说,平行光没有移动光源位置的区别,而点光源没有旋转光源的区别。

在模拟点光源的效果时,我们可以使用和平行光计算中一样的光照模型(包括漫反射光照模型和高光光照模型)。但是在点光源中,我们需要给出光源位置并计算光线方向。计算点光源的光线方向也很简单,就是取世界坐标系下的光源坐标减去世界坐标系下的物体坐标。下图表示了这个计算过程:

另外,由于点光源是有具体的位置的,你可以通过距离调节物体表面的光线强弱。点光源距离物体越远,物体表面越暗。下面的代码实现了当场景中只有一个点光源的时候的效果:

Listing 7.1 PointLight.fx(关于include文件Common.fxh如何引用请参考第八篇文章)

#include "include\\Common.fxh"

/*************** Resources ***************/
cbuffer CBufferPerFrame
{
    float4 AmbientColor : AMBIENT
    <
        string UIName = "Ambient Light";
        string UIWidget = "Color";
    >  = {1.0f, 1.0f, 1.0f, 0.0f};

    float4 LightColor : COLOR
    <
        string Object = "LightColor0";
        string UIName = "Light Color";
        string UIWidget = "Color";
    > = {1.0f, 1.0f, 1.0f, 1.0f};

    float3 LightPosition : POSITION
    <
        string Object = "PointLight0";
        string UIName = "Light Position";
        string Space = "World";
    > = {0.0f, 0.0f, 0.0f};

    float LightRadius
    <
        string UIName = "Light Radius";
        string UIWidget = "slider";
        float UIMin = 0.0;
        float UIMax = 100.0;
        float UIStep = 1.0;
    > = {10.0f};

    float3 CameraPosition : CAMERAPOSITION<string UIWidget="None";>;
}

cbuffer CBufferPerObject
{
    float4x4 WorldViewProjection : WORLDVIEWPROJECTION <string UIWidget="None";>;
    float4x4 World : WORLD <string UIWidget="None";>;

    float4 SpecularColor : SPECULAR
    <
        string UIName = "Specular Color";
        String UIWidget = "Color";
    > = {1.0f, 1.0f, 1.0f, 1.0f};

    float SpecularPower : SPECULARPOWER
    <
        string UIName = "Specular Power";
        string UIWidget = "Slider";
        float UIMin = 1.0;
        float UIMax = 255.0;
        float UIStep = 1.0;

    > = {25.0f};
}

Texture2D ColorTexture
<
    string ResourceName = "default_color.dds";
    string UIName = "Color Texture";
    string ResourceType = "2D";
>;

SamplerState ColorSampler
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = WRAP;
    AddressV = WRAP;
};

RasterizerState DisableCulling
{
    CullMode = NONE;
};

/*************** Data Structures ***************/
struct VS_INPUT
{
    float4 ObjectPosition : POSITION;
    float2 TextureCoordinate : TEXCOORD;
    float3 Normal : NORMAL;
};

struct VS_OUTPUT
{
    float4 Position : SV_Position;
    float3 Normal : NORMAL;
    float2 TextureCoordinate : TEXCOORD0;
    float4 LightDirection : TEXCOORD1;
    float3 ViewDirection : TEXCOORD2;
};

/*************** Vertex Shader ***************/
VS_OUTPUT vertex_shader(VS_INPUT IN)
{
    VS_OUTPUT OUT = (VS_OUTPUT)0;

    OUT.Position = mul(IN.ObjectPosition, WorldViewProjection);
    OUT.TextureCoordinate = get_corrected_texture_coordinate(IN.TextureCoordinate);
    OUT.Normal = normalize(mul(float4(IN.Normal, 0), World).xyz);

    float3 worldPosition = mul(IN.ObjectPosition, World).xyz;
    float3 lightDirection = LightPosition - worldPosition;
    OUT.LightDirection.xyz = normalize(lightDirection);
    OUT.LightDirection.w = saturate(1.0f -(length(lightDirection)/LightRadius));//Attenuation

    OUT.ViewDirection = normalize(CameraPosition - worldPosition);

    return OUT;
}

/*************** Pixel Shader ***************/
float4 pixel_shader(VS_OUTPUT IN) : SV_Target
{
    float4 OUT = (float4)0;

    float3 normal = normalize(IN.Normal);
    float3 lightDirection = normalize(IN.LightDirection);
    float3 viewDirection = normalize(IN.ViewDirection);
    float n_dot_1 = dot(normal, lightDirection);
    float3 halfVector = normalize(lightDirection + viewDirection);
    float n_dot_h = dot(normal, halfVector);

    float4 color = ColorTexture.Sample(ColorSampler, IN.TextureCoordinate);
    float4 lightCoefficients = lit(n_dot_1, n_dot_h, SpecularPower);

    float3 ambient = get_vector_color_contribution(AmbientColor, color.rgb);
    float3 diffuse = get_vector_color_contribution(LightColor, lightCoefficients.y * color.rgb) * IN.LightDirection.w;
    float3 specular = get_scalar_color_contribution(SpecularColor, min(lightCoefficients.z, color.w)) * IN.LightDirection.w;

    OUT.rgb = ambient + diffuse + specular;
    OUT.a = 1.0f;

    return OUT;
}

/*************** Techniques ***************/
technique10 main10
{
    pass p0
    {
        SetVertexShader(CompileShader(vs_4_0, vertex_shader()));
        SetGeometryShader(NULL);
        SetPixelShader(CompileShader(ps_4_0, pixel_shader()));

        SetRasterizerState(DisableCulling);

    }
}float3 specular = get_scalar_color_contribution(SpecularColor, min(lightCoefficients.z, color.w));

    OUT.rgb = ambient + diffuse + specular;
    OUT.a = 1.0f;

    return OUT;
}

/*************** Techniques ***************/
technique10 main10
{
    pass p0
    {
        SetVertexShader(CompileShader(vs_4_0, vertex_shader()));
        SetGeometryShader(NULL);
        SetPixelShader(CompileShader(ps_4_0, pixel_shader()));

        SetRasterizerState(DisableCulling);

    }
}

(1) Point Light Preamble : 点光源变量准备

这段代码中的大部分代码是从上一篇文章中的blinn-phong模型下的代码复制过来的,如有不明白的代码可以翻看之前的文章。

CBufferPerFrame代码块中的LightPosition表示点光源在场景中世界坐标系下的位置。LightRadius表示点光源的作用距离,超过这个距离,点光源将不会造成任何影响。

与之前相比,顶点着色器输出结构体中的LightDirection从float3变成了float4,多出来的第四个通道是用来存储光线的强弱的。也就是说光线的强弱和方向都是在顶点着色器中计算出来的。

注意
      将光线强度保存在LightDirection中的w通道只是为了节省性能,在这个变量中,xyz足以表示光线方向,而w一般是不会再用到的了。因此,与其再为了这个数据多占用输出结构中的一个变量,不如直接将其保存在w中。但光线强弱和光线方向是没有任何关系的。

(2) Point Light Vertex and Pixel Shader : 点光源顶点及像素着色器的实现

顶点着色器中,计算出了光线方向并将其保存在LightDirection变量的xyz中。光线强度通过以下公式计算得出:

公式中的lightDirection是直接通过顶点坐标和光源位置计算出的,还没有单位化,因此可以通过除以lightRadius得出光线强弱比例。计算得出的attenuation通过saturate函数保证其结果不会小于0。

像素着色器中并不需要做特殊处理,直接将顶点着色器中计算出的光线强度加入到漫反射和高光的计算中。

(3) Point Light Output : 点光源输出效果

下图展示了一个只带有点光源的场景的显示效果,左图中的球体离点光源距离较近,右图中则较远。图中黄色线框所表示的就是一个点光源的位置:

(4) Point Light Modifications : 修改点光源的实现

从下面的左右两张图中可以发现高光点效果的不同,这是因为之前使用顶点着色器计算点光源的时候会产生的一些瑕疵。这种瑕疵只会出现在当点光源距离模型表面十分接近时,这个时候光向量在根据顶点计算时顶点间的向量差异会更大,而顶点间的这些像素会继续使用顶点上计算出的这些光向量数据。

代码段Listing 7.2 PointLight-Imp.fx

#include "include\\Common.fxh"

/*************** Resources ***************/
cbuffer CBufferPerFrame
{
    float4 AmbientColor : AMBIENT
    <
        string UIName = "Ambient Light";
        string UIWidget = "Color";
    >  = {1.0f, 1.0f, 1.0f, 0.0f};

    float4 LightColor : COLOR
    <
        string Object = "LightColor0";
        string UIName = "Light Color";
        string UIWidget = "Color";
    > = {1.0f, 1.0f, 1.0f, 1.0f};

    float3 LightPosition : POSITION
    <
        string Object = "PointLight0";
        string UIName = "Light Position";
        string Space = "World";
    > = {0.0f, 0.0f, 0.0f};

    float LightRadius
    <
        string UIName = "Light Radius";
        string UIWidget = "slider";
        float UIMin = 0.0;
        float UIMax = 100.0;
        float UIStep = 1.0;
    > = {10.0f};

    float3 CameraPosition : CAMERAPOSITION<string UIWidget="None";>;
}

cbuffer CBufferPerObject
{
    float4x4 WorldViewProjection : WORLDVIEWPROJECTION <string UIWidget="None";>;
    float4x4 World : WORLD <string UIWidget="None";>;

    float4 SpecularColor : SPECULAR
    <
        string UIName = "Specular Color";
        String UIWidget = "Color";
    > = {1.0f, 1.0f, 1.0f, 1.0f};

    float SpecularPower : SPECULARPOWER
    <
        string UIName = "Specular Power";
        string UIWidget = "Slider";
        float UIMin = 1.0;
        float UIMax = 255.0;
        float UIStep = 1.0;

    > = {25.0f};
}

Texture2D ColorTexture
<
    string ResourceName = "default_color.dds";
    string UIName = "Color Texture";
    string ResourceType = "2D";
>;

SamplerState ColorSampler
{
    Filter = MIN_MAG_MIP_LINEAR;
    AddressU = WRAP;
    AddressV = WRAP;
};

RasterizerState DisableCulling
{
    CullMode = NONE;
};

/*************** Data Structures ***************/
struct VS_INPUT
{
    float4 ObjectPosition : POSITION;
    float2 TextureCoordinate : TEXCOORD;
    float3 Normal : NORMAL;
};

struct VS_OUTPUT
{
    float4 Position : SV_Position;
    float3 Normal : NORMAL;
    float2 TextureCoordinate : TEXCOORD0;
    float3 WorldPosition : TEXCOORD1;
    float3 Attenuation : TEXCOORD2;
};

/*************** Vertex Shader ***************/
VS_OUTPUT vertex_shader(VS_INPUT IN)
{
    VS_OUTPUT OUT = (VS_OUTPUT)0;

    OUT.Position = mul(IN.ObjectPosition, WorldViewProjection);
    OUT.WorldPosition = mul(IN.ObjectPosition, World).xyz;
    OUT.TextureCoordinate = get_corrected_texture_coordinate(IN.TextureCoordinate);
    OUT.Normal = normalize(mul(float4(IN.Normal, 0), World).xyz);

    float3 lightDirection = LightPosition - OUT.WorldPosition;

    OUT.Attenuation = saturate(1.0f - (length(lightDirection)/LightRadius));

    return OUT;
}

/*************** Pixel Shader ***************/
float4 pixel_shader(VS_OUTPUT IN) : SV_Target
{
    float4 OUT = (float4)0;

    float3 normal = normalize(IN.Normal);
    float3 lightDirection = normalize(LightPosition - IN.WorldPosition);
    float3 viewDirection = normalize(CameraPosition - IN.WorldPosition);

    float n_dot_1 = dot(normal, lightDirection);
    float3 halfVector = normalize(lightDirection + viewDirection);
    float n_dot_h = dot(normal, halfVector);

    float4 color = ColorTexture.Sample(ColorSampler, IN.TextureCoordinate);
    float4 lightCoefficients = lit(n_dot_1, n_dot_h, SpecularPower);

    float3 ambient = get_vector_color_contribution(AmbientColor, color.rgb);
    float3 diffuse = get_vector_color_contribution(LightColor, lightCoefficients.y * color.rgb) * IN.Attenuation;
    float3 specular = get_scalar_color_contribution(SpecularColor, min(lightCoefficients.z, color.w)) * IN.Attenuation;

    OUT.rgb = ambient + diffuse + specular;
    OUT.a = 1.0f;

    return OUT;
}

/*************** Techniques ***************/
technique10 main10
{
    pass p0
    {
        SetVertexShader(CompileShader(vs_4_0, vertex_shader()));
        SetGeometryShader(NULL);
        SetPixelShader(CompileShader(ps_4_0, pixel_shader()));

        SetRasterizerState(DisableCulling);

    }
}

在这段代码中,将光向量和视线向量移到像素着色器中计算,这样加大了计算量,但同时也是显示结果更为精细。但并没有将光线强度也加入到像素着色器中,因为当距离足够近时,两个顶点间的光线强度并不会差很多。虽然这样计算了两次光线方向,一次是在顶点着色器中为了计算光线强度的,一次是在像素着色器中计算每个像素点上的光线方向。但这样也比把所有计算都移到像素着色器中要更能节省一点性能。

时间: 2024-09-14 11:20:35

【DirectX11】第十篇 其他灯光类型——点光源的相关文章

Cocos2d-x3.0游戏实例之《别救我》第十篇(完结)——用Json配置各类型怪物数据

现在我们有2种类型的怪物,而且创建的时候是写死在代码里的,这是要作死的节奏~ 所以,必须可配置,不然会累死人的. 笨木头花心贡献,啥?花心?不呢,是用心~ 转载请注明,原文地址: http://www.benmutou.com/blog/archives/949  文章来源:笨木头与游戏开发 Json文件 什么是Json文件?说白了,它就是一个文本文档,只不过它的内容是按照一定的规则填写的. 于是,我们就可以按照那个规则去读取这份文档. 这,就是配置文件产生的缘由(才怪). (小若:才怪是什么意

第十篇 面向对象的程序设计

第十篇 面向对象的程序设计 阅读目录 一 面向对象的程序设计的由来 二 什么是面向对象的程序设计及为什么要有它 三 类和对象 3.1 什么是对象,什么是类 3.2 类相关知识 3.3 对象相关知识 3.4 对象之间的交互 3.5 类名称空间与对象/实例名称空间 3.6 小结 四 继承与派生 4.1 什么是继承 4.2 继承与抽象(先抽象再继承) 4.3 继承与重用性 4.4 组合与重用性 4.5 接口与归一化设计 4.6 抽象类 4.7 继承实现的原理(继承顺序) 4.8 子类中调用父类方法 五

Python开发【第二十篇】:缓存

Python开发[第二十篇]:缓存redis&Memcache 点击这里 Python之路[第九篇]:Python操作 RabbitMQ.Redis.Memcache.SQLAlchemy Memcached Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态.数据库驱动网站的速度.Memcached基于一个存储键/值对的hashmap.其守护进程(daemon )是用C写的,但是客户端可

SQL Serverf 索引 - 索引压缩 、附加特性 &lt;第十篇&gt;

一.索引压缩 数据和索引压缩在SQL Server2008被引入.压缩一个索引意味着将在一个页面中获得更多的关键字信息.这可以造成重大的性能改进,因为存储索引需要的页面和索引级别更少.因为索引中的键值被压缩和解压缩,也将造成CPU和内存的开销,所以这并不是适合所有索引的方案. 默认情况下,索引将不会被压缩.必须明确地在创建索引时要求索引被压缩.有两种压缩类型:行级压缩和页面级压缩.索引中的非叶子页面不接受页面类型压缩. 创建压缩索引的语法如下: CREATE NONCLUSTERED INDEX

解剖SQLSERVER 第十篇 OrcaMDF Studio 发布+ 特性重温(译)

原文:解剖SQLSERVER 第十篇 OrcaMDF Studio 发布+ 特性重温(译) 解剖SQLSERVER 第十篇  OrcaMDF Studio 发布+ 特性重温(译) http://improve.dk/orcamdf-studio-release-feature-recap/ 自从我上次作了一个OrcaMDF特性概述之后,两个半月过去了. 只是两个半月过去了自从我上次一个OrcaMDF特性概述.从那时起我一直在忙着参加SQLSERVER的最顶级的三个会议:SQLBits.SQLPA

第十篇 Integration Services:高级事件行为

本篇文章是Integration Services系列的第十篇,详细内容请参考原文. 简介在前一篇, we introduced fault tolerance by examining methods of task execution state management using the MaximumErrorCount and ForceExecutionResult properties.我们还学习了SSIS控制流任务错误.事件处理程序和容器之间的关系.这一篇我们重点关注事件的行为.在

全栈JavaScript之路(十)学习 DocumentFragment 类型 节点

DocumentFragment 类型节点,代表一个文档片段,是一种轻量级的'文档' 对象,可以包含其它类型节点,并有能力访问.操作其中的节点,但是在文档中没有文档标记,相当于是一个页面不可见的容器.其构造函数为,function DocumentFragment() {[native code]}. DocumentFragment 类型节点的特征; nodeType:11 nodeName:#document-fragment nodeValue:null prasentNode:null

第十篇 SQL Server代理使用代理帐户

本篇文章是SQL Server代理系列的第十篇,详细内容请参考原文 在这系列的前几篇,你查看了msdb库下用于授权访问SQL Server代理的安全角色.这些角色包括SQLAgentUserRole.SQLAgentReaderRole和SQLAgentOperatorRole.每个角色授予用户一定的权限来使用SQL Server代理,而不必是sysadmin服务器角色的成员.为完全管理控制SQL Server代理,你仍然需要sysadmin角色的成员.此外,你回顾了SQL Server代理服务

c++第十四章-(类型强转换)

类型强转换有2种. class Company { public: Company(std::string theName,std::string theProduct); virtual void printInfo(); protected: std::string name; std::string product; }; Company::Company(std::string theName,std::string theProduct) { this->name = theName;