3D编程:Diffuse Lighting(漫反射光)

Diffuse Lighting(漫反射光)

不同的材质表面反射光的方式也不同。在镜面上光的反射角度与入身角度相等。当在一只猫的眼睛里看到一束怪异的光芒,这就是光的反射性:这是由于猫的眼睛反射光的方向与光源的照射方向平行,但是方向相反。漫反射表面对光的反射在各个方向上都一样。

近似计算一个漫反射光,最简单并且最常用的模型是Lambert’s cosine law(朗伯余弦定律)。根据Lambert’s cosine law,照射到材质表面的光照亮度,与光源方向向量和面法线的夹角的余弦成正比。光源向量描述了光的照射方向,法向量确定了表面的朝向。图6.3说明了这些术语。

图6.3 An illustration of a surface normal, a light vector, and Lambert’s cosine law.

回想一下第二章,“A 3D/Math Primer”,所讨论的向量运算,通过向量的dot product可以得到光源向量与法向量(这两个向量都是单位向量)夹角的余弦值。一般的,法向量在3D object加载时由每一个vertex提供(或者通过triangle两条边的cross-product计算得出)。

Directional Lights

在3D图形学中定义了三种常用的光源:directional lights,point lights,spotlights(方向光,点光源,聚光灯)。一个directional light表示距离3D objects无穷远的光源,也就是说相对于objects没有坐标位置的意义。因此,照射到objects上的光线都是平行的,来自于同一个方向。Directional light的一个很好的例子是太阳光(虽然太阳不是严格意义上的无穷远)。图6.4描述了direction light的概念。

图6.4 An illustration of a directional light.

模拟一个directional light,只需要简单的用一个三维向量定义光的方向。也可以像ambient light那样,包括光的颜色和强度。列表6.2列出了一种diffuse lighting effect的代码,该effect使用了一种简单的directional light。本书中讲述的十分有限,建议把代码拷贝到NVIDIA FX Composer中,一步一步的测试该lighting effect。(也可以,从本书的配套网站上下载该代码)

列表6.2 DiffuseLighting.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 LightDirection : DIRECTION <
		string Object = "DirectionalLight0";
		string UIName =  "Light Direction";
		string Space = "World";
	> = {0.0f, 0.0f, -1.0f};
}

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

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 LightDirection : TEXCOORD1;
};

/************* 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);
	OUT.LightDirection = normalize(-LightDirection);

	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);
	float n_dot_l = dot(lightDirection, normal);

	float4 color = ColorTexture.Sample(ColorSampler, IN.TextureCoordinate);
	float3 ambient = AmbientColor.rgb * AmbientColor.a * color.rgb;

	float3 diffuse = (float3)0;

	if (n_dot_l > 0)
	{
		diffuse = LightColor.rgb * LightColor.a * n_dot_l * color.rgb;
	}

	OUT.rgb = ambient + diffuse;
	OUT.a = color.a;

	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);
    }
}

Diffuse Lighting Effect Preamble

DiffuseLighting.fx代码的第一行使用了一种C风格的#include方法,包含了一个头文件,该头文件中提供了一些通用的函数,用于越来越丰富的effects中。列表6.3列出了头文件Common.fxh的内容,头文件中带有_COMMON_FXH宏定义的头文件卫士,以及从之前的代码中转移过来的FLIP_TEXTURE_Y宏和get_corrected_texture_coordinate()函数。

列表6.3 Common.fxh

#ifndef _COMMON_FXH
#define _COMMON_FXH

/************* Constants *************/

#define FLIP_TEXTURE_Y 1

/************* Utility Functions *************/

float2 get_corrected_texture_coordinate(float2 textureCoordinate)
{
    #if FLIP_TEXTURE_Y
        return float2(textureCoordinate.x, 1.0 - textureCoordinate.y);
    #else
        return textureCoordinate;
    #endif
}

float3 get_vector_color_contribution(float4 light, float3 color)
{
    // Color (.rgb) * Intensity (.a)
    return light.rgb * light.a * color;
}

float3 get_scalar_color_contribution(float4 light, float color)
{
    // Color (.rgb) * Intensity (.a)
    return light.rgb * light.a * color;
}

#endif /* _COMMON_FXH */

另外需要注意的是两个新的CBufferPerFrame成员:LightColor和LightDirection。LightColor常量与AmbientColor具有同样的功能:用于表示directional light的颜色和强度。LightDirection存储了光源在world space中的方向。这两个新的shader constants都有对应的Object annotations,表示在NVIDIA FX Composer中可以把该变量与场景中的一个oject绑定。具体地说,就是可以在NVIDIA FX Composer的Render panel中放置光源,并把这些光源与带有Object annotation的shader constants关联起来。

最后需要注意的是,在CBufferPerObject中增加的World变量。该变量值与VS_INPUT结构体中新加的Normal成员变量有关。与object的vertices一样,面的法线存储在object space中。计算pixel的diffuse color是通过把法线向量与光的方向向量进行dot-product,但由于光源的方向是在world space中,因此法向量也要变换到wrold space中,而World矩阵就是用于这种变换。之所以不能使用组合矩阵World-View-Projection进行变换,是因为该矩阵是变换到homogeneous space而不仅仅是world space。World矩阵有可能包含了scaling变换,而面的法向量必须是一个规范化向量;因此,在变换后必须再进行normalizing(规范化)。

Diffuse Lighting Vertex Shader

接下来,讲解VS_OUTPUT结构体的两个新成员变量:Normal和LightDirection。Normal用于从CPU中传递变换后的面法线向量,而LightDirection有点特别,因为有一个shader constant叫LightDirection。LightDirection shader constant是一个global变量,存储了光源的方向,而VS_OUTPUT中的LightDirection成员表示object表面的光线方向。因此,在vertex shader中对global LightDirection取反,并赋值给对应的输出成员LightDirection。当然,可以在CPU中计算正确的光线方向,再传递到vertex shader中,这样在vertex shader中就不用进行取反操作。但是在使用NVIDIA FX Composer的情况下,必须这样做,因为光照的数据都由FX Composer发送到shader中,要想在Render panel中得到正确的预览效果必须要在shader对光线方向向量进行取反。对LightDirection取反时,还进行了normalize()操作,这是为了保证light direction向量是规范化的,如果可以保证从CPU传递过来的数据已经是规范化的,就可以省略normalize()了。

Diffuse Lighting Pixel Shader

尽管与ambient lighting effect有一些相似的地方,但是diffuse lighting pixel shader(见列表6.4)包含了更多新的代码。

列表6.4 The Pixel Shader from DiffuseLighting.fx

float4 pixel_shader(VS_OUTPUT IN) : SV_Target
{
	float4 OUT = (float4)0;

	float3 normal = normalize(IN.Normal);
    float3 lightDirection = normalize(IN.LightDirection);
	float n_dot_l = dot(lightDirection, normal);

	float4 color = ColorTexture.Sample(ColorSampler, IN.TextureCoordinate);
	float3 ambient = AmbientColor.rgb * AmbientColor.a * color.rgb;

	float3 diffuse = (float3)0;

	if (n_dot_l > 0)
	{
		diffuse = LightColor.rgb * LightColor.a * n_dot_l * color.rgb;
	}

	OUT.rgb = ambient + diffuse;
	OUT.a = color.a;

	return OUT;
}

首先,对于textrue sampling和calculation of the ambient引入了两个局部变量color和ambient。虽然有一点点改进,但与之前的步骤基本一样;其中的ambient变量被分离出来用于进一步计算最终的pixel color。

接下来介绍输入参数的成员变量Normal和LightDirection的规范化。传递到rasterizer阶段的数据是经过插值计算的,而该运算过程会导致向量变成非规范化的。与相应的视觉效果一样,插值运算导致的差错是微小的。因此,如果对性能有比较严格的要求,只需要简单地省略这些规范化运算即可。

下一步,把light direction和surface normal向量的dot product结果赋值给局部变量n_dot_l,用于下面计算diffuse color。其中if-statement(if条件语句)确保变量n_dot_l的值大于0。如果dot product为负值,表示光源在表面的背面,因此就不需要计算。正确的dot product值应该介于0.0和1.0之间。值为0.0表示光源的方向与表面平行(也就没有光照效果),值为1.0表示光源方向与表面垂直(能得到最在强度的光照)。而局部变量diffuse的值则由sampled color和directional light color,intensity以及前面计算的dot product相乘得到。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索string
, 变量
, float
, color
, 向量
, 方向
shader fx
漫反射光纤、光的漫反射、漫反射光源、激光漫反射、激光漫反射光电开关,以便于您获取更多的相关知识。

时间: 2024-10-31 14:53:44

3D编程:Diffuse Lighting(漫反射光)的相关文章

3D编程:第九章 Normal Mapping and Displacement Mapping

第九章 Normal Mapping and Displacement Mapping 本章主要讲述两种图形学技术,支持在不增加objects的poly primitive的情况下,在场景中增加更多的细节.第一种是normal mapping,通过创建一些"fake" geometry(虚设的多边形图元)模拟光照作用.第二种是displacement mapping,根据纹理数据moving vertices actually(与"fake"相对应,这里指真实的移动

J2ME 3D编程——第一个3D程序

编程|程序 J2ME 3D编程--第一个3D程序(附源代码) 更多文章请访问:http://blog.csdn.net/mailbomb 参考WTK2.2提供的demo,完成了第一个3D程序,虽然很简单,而且有些问题还不是很清楚,还是把代码共享出来和愿意学习J2ME 3D编程的朋友一起学习. 关于3D编程的资料,可以查阅我的blog. 关于代码的编译和运行说明如下: 1. 以下代码在J2ME WTK2.2下面编译通过. 2. 代码分为两个文件:First3DCanvas.java和First3D

J2ME 3D编程——第一个3D程序(附源代码)

参考WTK2.2提供的demo,完成了第一个3D程序,虽然很简单,而且有些问题还不是很清楚,还是把代码共享出来和愿意学习J2ME 3D编程的朋友一起学习. 关于3D编程的资料,可以查阅我的blog. 关于代码的编译和运行说明如下: 1.以下代码在J2ME WTK2.2下面编译通过. 2.代码分为两个文件:First3DCanvas.java和First3DMIDlet.java. 3.使用J2ME WTK2.2建立新的工程,主MIDlet类为:first3d. First3DMIDlet 4.将

《OpenGL超级宝典(第5版)》——第1章,第1.4节3D编程的基本原则

1.4 3D编程的基本原则 现在,我们对实时3D的基本概念已经有了相当程度的认识.我们讨论了一些术语以及PC上的一些示例应用程序.那么,如何在自己的计算机上创建这样的图像呢?好吧,这正是本书剩余部分的任务所在.不过,读者还需要知道一些基础知识,这正是我们接下来将要讨论的. 1.4.1 并非工具包 OpenGL基本上是一种底层渲染API(应用程序接口).我们不能告诉它"在什么地方绘制什么"--我们需要自己动手,通过载入三角形,应用必要的变换和正确的纹理.着色器并在必要时应用混合模式来组合

3D编程:第7章 Additional Lighting Models

第7章 Additional Lighting Models 本章在上一章光照模型的基础上再介绍point lights,spotlights以及在场景中添加多种光源.在讲解过程中,将会进一步学习HLSL和effect框架. Point Light 场景中的一个point light就好比在你周围的某个位置有一个灯泡,灯泡的光线会辐射到四面八方.这与directional light完全相反,directional light处于无穷远,并且光线来自于同一个方向.Directional ligh

3D编程:第六章 Lighting Models

第六章 Lighting Models 在现实世界中,没有光照是无法看见东西的:一个物体能被看见,要么是通过反射光源,要么是自身发光.在使用计算机渲染时,模拟光线的交互可以使3D objects更逼真.但是光照的交互是一个非常复杂的过程,不能简单的在各种交叉的帧率之间进行复制(至少目前阶段还不行).因此,使用光照与3D objects交互的近似值或光照模型,在场景中实现更多的细节.本章主要介绍一些基础的光照模型. Ambient Lighting(环境光) 在一个光照环境中,环境光看起来远处不在

手机上J2ME的3D编程-简单创建3D立方体(1)

编程|创建 参考文档:JSR-184 工具:Eclipse3.0+Eclipseme API:Mobile 3D Graphics API 模拟器:Sony Ericsson 分析: 在现实生活中,我们所看到的是一个3维系统.透过双眼,可以看到的是一个丰富多彩的世界.我们所用的照相机,能够拍出很多漂亮的图片.拍摄每张图片时,可以根据拍摄距离的远近可以调整所拍摄画面上的内容. 在虚拟的世界里,可以模拟真实世界.欲将虚拟世界展示出来,就要利用一个虚拟的照相机,将虚拟的世界拍摄出来. 在JSR-184

3D编程:The Direct3D 11 Graphics Pipeline

Direct3D 11图形管线 个人电脑一般都有两个处理器:CPU和GPU,你需要对每一个处理器编写代码.这两个部件拥有完全不同的架构和指令集.在图形编程中,你需要对两个处理器都都编写软件,对于CPU应用程序使用通用的语言,比如C++,对于GPU则使用HLSL.DirectX是系统之间的桥梁.大部分关于图形编程的文章都集中讲述CPU部分或者GPU部分,但是这两部分都是非常难懂的.本书两个部分都会讲解.         在DirectX里面我们主要学习Direct3D.简单来说,Direct3D是

3D编程:第一章 Introducing DirectX

DirectX是一组API的集合,用于在微软平台上,包括Windows,Windows Phone,Xbox 360,以及Xbox One,开发游戏或图形相关的应用程序.DirectX从1990年代中期发展至今,并处于现在图形开发的前沿.Direct3D是DirectX里面一套3D图形API,是这本书主要讲述的.DirectX也包含了2D图形,输入,音频,文字渲染和GPU编程的系统支持.本章概述了DirectX和DirectX 11的图形管线. 回顾历史 DirectX 1.0版本发布于1995