我正在尝试实现BlinnPhong阴影在纽比为教育目的。不过,几天来我一直在调试参数。在
我的总体想法如下。因为方程是针对一个通道给出的。我将模型应用于每个颜色通道,以获得通道中的相对像素强度,然后将通道重新组合到一起以获得所有图像。在
我的朗伯系数似乎没有考虑到光线位置的变化,但它确实改变了像素强度,但其他参数对输出几乎没有影响。 任何帮助都将不胜感激。 下面是代码的相关位(对于感兴趣的人,完整代码是here):
def normalize_1d_array(arr):
"Normalize 1d array"
assert arr.ndim == 1
result = None
if np.linalg.norm(arr) == 0:
result = arr
else:
result = arr / np.linalg.norm(arr)
return result
def normalize_3col_array(arr):
"Normalize 3 column array"
assert arr.shape[1] == 3
assert arr.ndim == 2
normal = np.copy(arr)
normal[:, 0] = normalize_1d_array(normal[:, 0])
normal[:, 1] = normalize_1d_array(normal[:, 1])
normal[:, 2] = normalize_1d_array(normal[:, 2])
return normal
def get_vector_dot(arr1, arr2):
"Get vector dot product for 2 matrices"
assert arr1.shape == arr2.shape
newarr = np.sum(arr1 * arr2, axis=1, dtype=np.float32)
return newarr
class LightSource:
"Simple implementation of a light source"
def __init__(self,
x=10.0, # x
y=5.0, # y
z=0.0, # light source at infinity
intensity=1.0, # I_p
ambient_intensity=1.0, # I_a
ambient_coefficient=0.1, # k_a
light_power=80.0):
"light source"
self.x = x
self.y = y
if z is not None:
assert isinstance(z, float)
self.z = z
self.intensity = intensity
self.power = light_power
self.ambient_intensity = ambient_intensity # I_a
self.ambient_coefficient = ambient_coefficient # k_a
# k_a can be tuned if the material is known
def copy(self):
"copy self"
return LightSource(x=self.x,
y=self.y,
z=self.z,
intensity=self.intensity,
light_power=self.power)
class ChannelShader:
"Shades channels"
def __init__(self,
coordarr: np.ndarray,
light_source: LightSource, # has I_a, I_p, k_a
surface_normal: np.ndarray,
imagesize: (int, int),
color: np.ndarray, # they are assumed to be O_d and O_s
spec_coeff=0.5, # k_s
screen_gamma=2.2,
diffuse_coeff=0.9, # k_d
attenuation_c1=2.0, # f_attr c1
attenuation_c2=0.0, # f_attr c2 d_L coefficient
attenuation_c3=0.0, # f_attr c3 d_L^2 coefficient
shininess=270.0 # n
):
self.light_source = light_source
self.light_intensity = self.light_source.intensity # I_p
self.ambient_coefficient = self.light_source.ambient_coefficient # k_a
self.ambient_intensity = self.light_source.ambient_intensity # I_a
self.coordarr = coordarr
self.surface_normal = np.copy(surface_normal)
self.screen_gamma = screen_gamma
self.shininess = shininess
self.diffuse_coeff = diffuse_coeff # k_d
self.diffuse_color = normalize_1d_array(color) # O_d: object diffuse color
self.spec_color = normalize_1d_array(color) # O_s: object specular color
self.spec_coeff = spec_coeff # k_s: specular coefficient
self.imsize = imagesize
self.att_c1 = attenuation_c1
self.att_c2 = attenuation_c2
self.att_c3 = attenuation_c3
def copy(self):
return ChannelShader(coordarr=np.copy(self.coordarr),
light_source=self.light_source.copy(),
surface_normal=np.copy(self.surface_normal),
color=np.copy(self.diffuse_coeff) * 255.0)
@property
def distance(self):
yarr = self.coordarr[:, 0] # row nb
xarr = self.coordarr[:, 1] # col nb
xdist = (self.light_source.x - xarr)**2
ydist = (self.light_source.y - yarr)**2
return xdist + ydist
@property
def distance_factor(self):
resx = self.imsize[1]
factor = self.distance / self.light_source.z * resx
return 1.0 - factor
@property
def light_direction(self):
"get light direction matrix (-1, 3)"
yarr = self.coordarr[:, 0]
xarr = self.coordarr[:, 1]
xdiff = self.light_source.x - xarr
ydiff = self.light_source.y - yarr
light_matrix = np.zeros((self.coordarr.shape[0], 3))
light_matrix[:, 0] = ydiff
light_matrix[:, 1] = xdiff
light_matrix[:, 2] = self.light_source.z
# light_matrix[:, 2] = 0.0
return light_matrix
@property
def light_attenuation(self):
"""
Implementing from Foley JD 1996, p. 726
f_att : light source attenuation function:
f_att = min(\frac{1}{c_1 + c_2{\times}d_L + c_3{\times}d^2_{L}} , 1)
"""
second = self.att_c2 * self.distance
third = self.att_c3 * self.distance * self.distance
result = self.att_c1 + second + third
result = 1 / result
return np.where(result < 1, result, 1)
@property
def normalized_light_direction(self):
"Light Direction matrix normalized"
return normalize_3col_array(self.light_direction)
@property
def normalized_surface_normal(self):
return normalize_3col_array(self.surface_normal)
@property
def costheta(self):
"set costheta"
# pdb.set_trace()
costheta = get_vector_dot(
arr1=self.normalized_light_direction,
arr2=self.normalized_surface_normal)
# products of vectors
costheta = np.abs(costheta) # as per (Foley J.D, et.al. 1996, p. 724)
return costheta
@property
def ambient_term(self):
"Get the ambient term I_a * k_a * O_d"
term = self.ambient_coefficient * self.ambient_intensity
return term * self.diffuse_color
@property
def view_direction(self):
"Get view direction"
# pdb.set_trace()
cshape = self.coordarr.shape
coord = np.zeros((cshape[0], 3)) # x, y, z
coord[:, :2] = -self.coordarr
coord[:, 2] = 0.0 # viewer at infinity
coord = normalize_3col_array(coord)
return coord
@property
def half_direction(self):
"get half direction"
# pdb.set_trace()
arr = self.view_direction + self.normalized_light_direction
return normalize_3col_array(arr)
@property
def spec_angle(self):
"get spec angle"
specAngle = get_vector_dot(
arr1=self.half_direction,
arr2=self.normalized_surface_normal)
return np.where(specAngle > 0.0, specAngle, 0.0)
@property
def specular(self):
return self.spec_angle ** self.shininess
@property
def channel_color_blinn_phong(self):
"""compute new channel color intensities
Implements: Foley J.D. 1996 p. 730 - 731, variation on equation 16.15
"""
second = 1.0 # added for structuring code in this fashion, makes
# debugging easier
# lambertian terms
second *= self.diffuse_coeff # k_d
second *= self.costheta # (N \cdot L)
second *= self.light_intensity # I_p
# adding phong terms
second *= self.light_attenuation # f_attr
second *= self.diffuse_color # O_d
third = 1.0
third *= self.spec_color # O_s
third *= self.specular # (N \cdot H)^n
third *= self.spec_coeff # k_s
result = 0.0
result += self.ambient_term # I_a × k_a × O_d
result += second
result += third
pdb.set_trace()
return result
谢谢
毕竟,实现没有太多问题,但是我使用的图像需要非常奇怪的参数值,因为它们是在特定的条件下产生的。在
我使用过的大多数图像都包含粗糙的表面,并以未上釉的粘土为材料,这些图像是在一个有单一光源的受控环境中拍摄的,与真实世界中物体被多个光点照亮的环境相反。在
因此,大多数关于环境光照和镜面反射的参数在使用中没有多大意义。在
我将实现的相关部分放在这里作为未来用户的参考,请确保不要使用默认值。在
关于实施的一些细节:
它基本上遵循Foley J.D.中规定的公式。等,1996年,第730-731页,第16.15号,增加了一个中间向量,这是blinn phong所需要的。
ChannelShader
需要以下内容:(-1, 2)
(-1, 3)
(-1,)
LightSource
类型的光源如果要对多个通道着色,则可以对每个通道使用相同的曲面法线。在
最后要提醒的是,这是明显缓慢的,即使有纽比。 不过,与其他基于pupi的库一样,也没有像cupy等测试过的库
相关问题 更多 >
编程相关推荐