前几天算是弄懂了TBN矩阵然后写了一个Normal Mapping、写一写自己的理解吧

因为Normal Mapping的图是每个点的法向量,是以每个点为原点的,而点光源的位置啥的是在世界坐标系/物体坐标系,不是在当前计算的这个点的坐标系,所以在计算时用到法向量贴图的数据的话,必须把其他的元素和这个点法向量转换到同一坐标系 

Σ( ° △ °|||)︴

然后比较简单的一种转换方式就是建立这样的坐标系:以这个点为原点,UV方向为两个轴,UV方向的叉乘方向为第三个轴,然后这个点的法向量不变,其他元素的位置(比如点光源的坐标)转换到这个坐标系,进行计算。这个坐标系定义的空间就叫做Tangent Space,切向空间。TBN矩阵就是在世界空间和切向空间直接坐标转换的一个矩阵。

TBN矩阵中 ((据说是Tangent, Bitangent, Normal,一般只需要知道Tangent和Normal就可以了。在一些厉害的3D物体格式,比如fbx 3ds等,是可以在模型导出的时候通过建模软件直接导出Tangent和Normal的值的。然而,在obj格式的模型中,需要自己计算Tangent和Normal。

计算Normal的方法很简单,先求每个面的法向量,然后对于每个点,求跟它有关系的每个面的法向量的平均值,每个面的法向量只需要叉乘一下相邻两条边向量就好(不管是三角面片还是四元面片)。

计算Tangent的基本思想和Normal差不多,求每个面的Tangent,然后对于每个点求一个平均值。求每个面的Tangent就需要一些数学计算。原理就不说了,反正我大概意会了一下……这个教程讲的非常好http://www.terathon.com/code/tangent.html

贴个法相贴图的效果图

(o゚ω゚o) 

今天换了NVIDIA GeForce GTX750(

原来编译用的显卡是Intel HD Graphics 4600

)跑我的毕设程序,竟然Shader编译不过了。。。

一开始以为是显卡驱动的问题,重装驱动之后问题仍然存在、

后来发现是Shader文件的读入出错了

输出了一下报错信息: 

(0) : error C0000: syntax error, unexpected $end at token "<EOF>"

因为我在读入Shader文件的时候用的是fread()函数,读到最后一行的时候包括了换行符

后来用getline一行一行读并在适当的时候添加”n”,保证最后一行没有换行符,然后可以编译过了。。。


好坑啊QAQ

前言

这是我在大学修习逻辑与计算机设计基础、计算机组成和计算机体系结构三门硬件课的过程中积累的 Verilog 笔记。

Verilog 是一门主要用于逻辑电路设计的硬件描述语言。语言标准主要有 Verilog-1995Verilog-2001 两个版本,建议在创建工程时选择 Verilog-2001 标准以支持更多实用的语法。

虽然 Verilog 的语法与 C 相似,但是二者是面向各自的目标硬件设计的。Verilog 是一门面向逻辑电路的具体实现而设计的语言(正如 C 在某种程度上是面向汇编等底层实现的),因此写作 Verilog 时不可以将 C 等编程语言的思维方式代入,而是要始终清晰地思考正在编写的代码将能够综合成怎样的逻辑电路实现——如果可以,那么大多能够写出在字面和实现上都优雅的代码;如果不行,那么综合时大概也会报错或消耗大量资源,此时则应该考虑调整思路。

手册

Verilog 的标准文档 IEEE 1364-2001 在网络上可以找到下载,但相比之下,这份标准还是稍显冗长或不够友好。而在标准文档之外,Xilinx ISE 的 XST 综合器也提供了实现文档,并且其中包含了许多 Verilog 语法的实用描述,因此可以作为一本更加友好的手册进行查阅。

XST User Guide for Virtex-6, Spartan-6, and 7 Series Devices

以及这里还有一份简单的非官方 Verilog-2001 手册:

Verilog HDL Quick Reference Guide

指南

在本文草稿的同时,我的某位同学也完成了一份 Verilog 指南。指南十分详尽而实用,因此希望读者先行阅读。

Verilog General Guide

重复(Replication)

在 Verilog 中,你可以使用 {COUNT{singal}} 将指定的信号重复数次后连接。

例如,想要写一个 32 位 0/1 相见的字面量,不需要写出 32'b01010101010101010101010101010101,而是写 {16{2'b01}} 即可简洁而直观地完成。

另一个例子是 16 位到 32 位的符号扩展,也可以用 {16{signal[15]}, signal[15:0]} 来完成。

参数定义:parameter, localparam, `define

Verilog 提供了三种定义“常量”的方式。

parameter 是可以由模块外部改变的参数。除了在模块内部定义的语法,我更加推荐采用 Verilog-2001 中在模块接口声明中定义的语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
module module_name
#(
parameter parameter_name = parameter_default_value
) (
...
)

...
endmodule

module_name #(
.parameter_name(parameter_value)
) (
...
);

localparam 是模块内部定义的参数,语法如 localparam localparam_name = localparam_value

`define 是 Verilog 的宏定义,与 C 宏的文本替换相似。宏除了用于定义常量,还可以用来简化代码的编写,例如:

1
2
`define packed_wires = {wire_1, wire_2, wire_3}
packed_wires = 3'b101

变基部分选择(Indexed part-select)

可以使用 wire_name[index_wire_name] 的方式来实现一位宽度的多路选择器。相应地,Verilog-2001 也提供了 wire_name[WIDTH * index_wire_name +: WIDTH] 的方式来实现多位宽度的多路选择器。详情可以参考这个 StackOverflow 问题:

What is this operator called as “+:” in verilog – Electrical Engineering Stack Exchange

然而这个语法的综合有一些怪异的地方,有时会导致综合成移位器而非多路选择器,消耗较多的资源,使用时需要留意一下综合报告。

对表达式进行选择

在 Verilog 中,无法直接对 wire_name + 1'b1 这样的表达式选择某些位,但可以其实通过加上花括号的方式进行选择,例如 {wire_name + 1'b1}[3:0]。这个形式可以用于显式地截短运算结果并且不触发警告。

然而这个 trick 对于变基部分选择无效。以及似乎在某些旧型号的硬件上 XST 无法识别这个语法。

代码简化

可以采用 generatetaskfunction 简化代码逻辑的编写,详细的使用方法在 Verilog General Guide 中已经说明,故不再赘述。

信号宽度警告

信号宽度警告是一种十分有用的警告,因为许多低级错误都是由于错误地(或忘记)指定信号宽度导致的。请务必仔细检查综合报告。

由于裸写的非零十进制整数字面量默认为 32 位,容易增加多余的警告,因此建议在书写字面量时总是使用 2'b10'd16'h 这样的前缀形式。

至于对信号进行递增并且在溢出后自动归零,可以采用 reg_name = reg_name + 1'b1 的形式,综合器不会将此判断为溢出而发出警告。

组合逻辑

在 Verilog 中组合逻辑可以通过在 always @* 块中使用 ifswitch 和阻塞赋值 = 实现,也可以直接使用 assign?: 这个条件运算符实现。

我个人的建议是避免使用 always 块实现组合逻辑,因为在用 ifswitch 实现逻辑时,很容易忘记书写某些情况下的默认值,导致综合结果为 FF/Latch 的时序电路而非组合电路,并且这件事情很难在综合报告中发现。

我目前采取的实践是仅将 always 块用于实现时序电路,而使用条件运算符实现组合逻辑。

顺带提及,always @* 用于实现组合电路,应该只使用阻塞赋值 =always @(posedge clock) 用于实现时序电路,应该只使用非阻塞赋值 <=;两者混用依然可以成功综合,但是意义不明,十分容易出错。

Lint

对于长时间调试难以找出的错误,可以尝试使用 Lint 工具对代码进行静态检查,它比 XST 综合器的检查更加严格,也常常能指出代码中埋藏的许多低级错误(我曾经用它在十分钟内找出了一个调试了一整天未能找出的 Bug)。

Verilator 是一个免费的 Verilog 模拟器,其中也包含了 Lint 功能。安装 verilator后,执行 verilator --lint-only top.v 即可开始检查。

其他

综合过程中输出的 Simulation mismatch 警告不能无视,否则可能会导致不稳定的综合结果和奇怪的错误。

结语

除了某些语法检查不够严格,Verilog 其实是一门设计得较为直觉的语言,语法结构大多可以映射到电路实现,有着 C 一般的简洁和直观。如果遵循一定的编码规范和最佳实践,使用硬件而非软件的思维进行编码,还是可以较为简单地达成目标的。