Direct3D 11光栅化中16.8定点数的溢出规避方法问询
16.8定点数实现Direct3D 11光栅化的溢出规避方案
问题概述
需基于16.8定点数(整数部分16位带符号,小数部分8位)实现Direct3D 11风格光栅化,坐标范围限定为[-32768.0, 32767.99609375]。光栅化依赖边缘函数判断像素归属:
E(p) = (p.x - v0.x) * (v1.y - v0.y) - (p.y - v0.y) * (v1.x - v0.x)
(顶点按顺时针排列,E(p)≥0表示像素在三角形边的内侧)
直接计算时,边缘函数的乘法结果需32.16定点数才能避免溢出,但我们只能使用16.8定点数存储中间值。以下是针对你遇到的问题的可行解决方案,以及你提到的文章方法的详细解释:
可行解决方案
方案1:缩放边缘函数+增量累加
核心思路是通过预缩放将边缘函数的取值范围压缩到16.8定点数的有效区间内,同时利用增量累加避免重复计算的溢出:
- 确定缩放因子:计算三角形三个顶点对应的边缘函数绝对值的最大值
max_E(即三角形面积的2倍的最大值),取缩放因子scale = max_E / 32767.99609375(16.8定点数的最大值),确保缩放后的边缘函数最大值刚好适配16.8范围。 - 缩放边缘函数系数:将边缘函数的线性展开系数
A = v1.y - v0.y、B = v0.x - v1.x、C = v0.y*v1.x - v0.x*v1.y分别除以scale,得到16.8定点数格式的A_s、B_s、C_s。 - 初始值适配:计算包围盒左上角像素的边缘函数
E(p0),除以scale得到E_s(p0),用16.8定点数存储。若E_s(p0)超出16.8范围,直接判定该像素在三角形外。 - 增量计算:行内像素的边缘函数增量为
ΔE_s_x = A_s(x每增加1,边缘函数的变化量),跨行增量为ΔE_s_y = B_s(y每增加1的变化量),均为16.8格式。 - 扫描线遍历:逐行遍历包围盒内像素,每行从左到右累加
ΔE_s_x,每行开头从上一行累加ΔE_s_y,通过E_s(p)的符号判断像素是否在三角形内(≥0为内侧)。
方案2:归一化重心坐标+包围盒过滤
针对你之前尝试的归一化方法的缺陷,优化后可彻底避免包围盒内的溢出:
- 计算三角形总面积:取任意顶点对应的边缘函数绝对值(比如
E0 = |E(v1)|,即v1到v0v2边的边缘函数值,等于三角形面积的2倍)。 - 归一化边缘函数:将每个边缘函数的系数除以
E0,此时边缘函数值的范围为[0,1],对应重心坐标的比例值。 - 适配16.8格式:将归一化后的系数乘以
32767.99609375,转换为16.8定点数。 - 限定遍历范围:仅遍历三角形的最小包围盒内的像素(Direct3D 11光栅化的标准流程),此时所有计算出的边缘函数值都在
[0,1]缩放后的16.8范围内,完全不会溢出,直接通过符号判断像素归属即可。
你提到的文章方法详细解释
那篇文章中子像素精度段落末尾的方法,核心是只关注边缘函数的符号而非具体数值,用饱和累加规避溢出:
- 初始值截断:用32位定点数计算包围盒左上角像素的初始边缘函数
E(p0),仅保留低24位(对应16.8定点数的存储位)。若E(p0)的绝对值超过2^23(16.8最大值的2倍),直接判定该像素在三角形外。 - 饱和增量累加:计算16.8格式的行内增量
ΔE_x和跨行增量ΔE_y,累加时使用饱和加法:若累加结果超过16.8的最大值,直接设为最大值;若低于最小值,设为最小值。 - 符号判断有效性:由于我们只需要判断边缘函数的符号(正/负),饱和操作不会改变符号的正确性——饱和后的正值对应原边缘函数为正,负值对应原边缘函数为负,完全满足光栅化的判断需求。这种方法无需存储完整的32位初始值,仅用16.8定点数即可完成遍历。
内容的提问来源于stack exchange,提问作者quad
相关产品推荐
相关产品推荐

