摘要

着色模型(shading model)描述的是材质表面对光线的作用,它的输入是光线、入射角、反射角、材质的属性(例如粗糙度、金属度等),输出是材质的颜色。前面提到的BRDF模型就是shading model的一种,但是BRDF模型过于简单,很多复杂的物理现象无法描述。本文将接着BRDF继续介绍一些更复杂的shading model,以及UE4和Disney的实现。

本文主要介绍理论部分,下篇将介绍UE4、Filament以及Disney对shading model的详细实现。

理论

主要介绍对各向异性、次表面、清漆和布料几种物理现象的shading model。

Anisotropy

上图左:各向同性金属球;右:各向异性金属球。图片来源:Filament文档。

将各向同性的shading model拓展到各向异性,需要解决两个问题,

  1. 需要引入哪些新的参数来描述各向异性?
  2. 需要在原有BRDF模型的基础上修改公式的哪些部分来描述各向异性?

对于第一个问题,既然是“各向”异性,必然要分别定义各个方向上的差异的属性,这里的属性一般是粗糙度。方向一般在切平面上选取两个轴,即tangent方向和bitagent方向,他们与normal方向共通构成一组正交基底(TBN坐标系)。对于需要精细控制切平面方向的模型,比如头发,会单独提供切向贴图,指明切向方向,类似于法向贴图。其他情况一般由系统(shader)确定切向方向。所以总结下来,需要额外增加一个参数,将原有的粗糙度替换为tangent和bitangent方向的粗糙度(记为αt,αb\alpha_t,\alpha_bαt​,αb​或αx,αy\alpha_x,\alpha_yαx​,αy​)。

实际操作中,一般是增加一个anisotropy各向异性参数,然后用粗糙度α\alphaα(α=roughness∗roughness\alpha = roughness * roughnessα=roughness∗roughness)和anisotropy计算tangent/bitangent两个方向上的粗糙度,具体的映射方法有很多。

  • Neubelt and Pettine

αx=ααy=lerp(0,α,1−anisotropy)\begin{aligned} \alpha_x & = \alpha \\ \alpha_y & = lerp(0, \alpha, 1-anisotropy) \end{aligned} αx​αy​​=α=lerp(0,α,1−anisotropy)​

  • Burley

αx=α1−0.9×anisotropyαy=α1−0.9×anisotropy\begin{aligned} \alpha_x & = \frac{\alpha}{\sqrt{1-0.9\times anisotropy}} \\ \alpha_y & = \alpha\sqrt{1-0.9\times anisotropy} \end{aligned} αx​αy​​=1−0.9×anisotropy​α​=α1−0.9×anisotropy​​

  • Kulla

αx=α×(1+anisotropy)αy=α×(1−anisotropy)\begin{aligned} \alpha_x & = \alpha \times (1+anisotropy) \\ \alpha_y & = \alpha \times (1-anisotropy) \end{aligned} αx​αy​​=α×(1+anisotropy)=α×(1−anisotropy)​

对于第二个问题,BRDF公式取决于三个部分,法向分布函数DDD,遮挡项GGG,和表示微观BRDF的菲涅尔项FFF。其中FFF肯定是不会变的,因为各向异性是宏观的现象,不会影响微观表面的性质。至于DDD和GGG,由于GGG都是从DDD推导出来的,因此我们先看一下DDD需要作出怎样的修改。

形状不变性

法向分布函数DDD的形状不变性指的是这样一种特性,改变DDD的粗糙度系数相当于对微表面进行拉伸,而不改变微表面的形状。

要说明白形状不变性,我们引入一个新的函数P22(xm~,ym~)P^{22}(x_{\tilde{m}},y_{\tilde{m}})P22(xm~​,ym~​),描述微表面的法向量m{\bf{m}}m的2D斜率分布。其中,m=(xm,ym,zm){\bf{m}}=(x_m,y_m,z_m)m=(xm​,ym​,zm​)并且

(xm~,ym~)=(−xmzm,−ymzm)=−tan⁡θm(cos⁡ϕm,sin⁡ϕm)m=(−xm~,−ym~,1)xm~2+ym~2+1\begin{aligned} (x_{\tilde{m}},y_{\tilde{m}}) & =(-\frac{x_m}{z_m},-\frac{y_m}{z_m}) = -\tan\theta_m(\cos\phi_m, \sin\phi_m)\\ {\bf{m}} & =\frac{(-x_{\tilde{m}},-y_{\tilde{m}},1)}{\sqrt{x_{\tilde{m}}^2+y_{\tilde{m}}^2+1}} \end{aligned} (xm~​,ym~​)m​=(−zm​xm​​,−zm​ym​​)=−tanθm​(cosϕm​,sinϕm​)=xm~2​+ym~2​+1​(−xm~​,−ym~​,1)​​

m{\bf{m}}m的球坐标系坐标为(1,θ,ϕ)(1,\theta,\phi)(1,θ,ϕ),如下图所示。

既然是分布函数,P22(xm~,ym~)P^{22}(x_{\tilde{m}},y_{\tilde{m}})P22(xm~​,ym~​)满足:

∫−∞∞∫−∞∞P22(xm~,ym~)dxm~dym~=1\int^\infty_{-\infty}\int^\infty_{-\infty}P^{22}(x_{\tilde{m}},y_{\tilde{m}})dx_{\tilde{m}}dy_{\tilde{m}}=1 ∫−∞∞​∫−∞∞​P22(xm~​,ym~​)dxm~​dym~​=1

并且P22(xm~,ym~)P^{22}(x_{\tilde{m}},y_{\tilde{m}})P22(xm~​,ym~​)与DDD的关系为:

D(m)=χ+(n,m)cos⁡4θmP22(xm~,ym~)=χ+(n,m)(n⋅m)4P22(xm~,ym~)D({\bf{m}})=\frac{\chi^+({\bf{n}},{\bf{m}})}{\cos^4\theta_m}P^{22}(x_{\tilde{m}},y_{\tilde{m}})=\frac{\chi^+({\bf{n}},{\bf{m}})}{({\bf{n}}\cdot{\bf{m}})^4}P^{22}(x_{\tilde{m}},y_{\tilde{m}}) D(m)=cos4θm​χ+(n,m)​P22(xm~​,ym~​)=(n⋅m)4χ+(n,m)​P22(xm~​,ym~​)

考虑到P22(xm~,ym~)P^{22}(x_{\tilde{m}},y_{\tilde{m}})P22(xm~​,ym~​)同时也是粗糙度α\alphaα的函数,因此可以写为P22(xm~,ym~,α)P^{22}(x_{\tilde{m}},y_{\tilde{m}},\alpha)P22(xm~​,ym~​,α)。

满足形状不变性的法向分布函数,它的P22(xm~,ym~,α)P^{22}(x_{\tilde{m}},y_{\tilde{m}},\alpha)P22(xm~​,ym~​,α)符合这种形式:

P22(xm~,ym~,α)=1α2f(xm~2+ym~2α)=1α2f(tan⁡θmα)=1α2f(1−(n⋅m)2α(n⋅m))D(m)=χ+(n,m)(n⋅m)4P22(xm~,ym~)=χ+(n,m)α2(n⋅m)4f(tan⁡θmα)=χ+(n,m)α2(n⋅m)4f(1−(n⋅m)2α(n⋅m))\begin{aligned} P^{22}(x_{\tilde{m}},y_{\tilde{m}},\alpha) & =\frac{1}{\alpha^2}f\left(\frac{\sqrt{x_{\tilde{m}}^2+y_{\tilde{m}}^2}}{\alpha}\right)=\frac{1}{\alpha^2}f\left(\frac{\tan\theta_m}{\alpha}\right) \\ & = \frac{1}{\alpha^2}f\left(\frac{\sqrt{1-({\bf{n}}\cdot{\bf{m}})^2}}{\alpha({\bf{n}}\cdot{\bf{m}})}\right) \\ D({\bf{m}}) & =\frac{\chi^+({\bf{n}},{\bf{m}})}{({\bf{n}}\cdot{\bf{m}})^4}P^{22}(x_{\tilde{m}},y_{\tilde{m}})=\frac{\chi^+({\bf{n}},{\bf{m}})}{\alpha^2({\bf{n}}\cdot{\bf{m}})^4}f\left(\frac{\tan\theta_m}{\alpha}\right) \\ & = \frac{\chi^+({\bf{n}},{\bf{m}})}{\alpha^2({\bf{n}}\cdot{\bf{m}})^4}f\left(\frac{\sqrt{1-({\bf{n}}\cdot{\bf{m}})^2}}{\alpha({\bf{n}}\cdot{\bf{m}})}\right) \end{aligned} P22(xm~​,ym~​,α)D(m)​=α21​f(αxm~2​+ym~2​​​)=α21​f(αtanθm​​)=α21​f(α(n⋅m)1−(n⋅m)2​​)=(n⋅m)4χ+(n,m)​P22(xm~​,ym~​)=α2(n⋅m)4χ+(n,m)​f(αtanθm​​)=α2(n⋅m)4χ+(n,m)​f(α(n⋅m)1−(n⋅m)2​​)​

对于任意大于零的实数λ\lambdaλ,有:

P22(xm~,ym~,α)=1λ2P22(xm~λ,ym~λ,αλ)P^{22}(x_{\tilde{m}},y_{\tilde{m}},\alpha)=\frac{1}{\lambda^2}P^{22}(\frac{x_{\tilde{m}}}{\lambda},\frac{y_{\tilde{m}}}{\lambda},\frac{\alpha}{\lambda}) P22(xm~​,ym~​,α)=λ21​P22(λxm~​​,λym~​​,λα​)

也就是说,改变粗糙度仅相当于对P22(xm~,ym~,α)P^{22}(x_{\tilde{m}},y_{\tilde{m}},\alpha)P22(xm~​,ym~​,α)进行拉伸。

前面提到的三种法向分布函数:Beckmann、BlinnPhong和GGX,只有BlinnPhong不满足形状不变性。推广的GTR公式也同样不满足形状不变性。

各向异性D和G

对于具有形状不变性性质的法向分布函数DDD,它的形式变为:

D(m)=χ+(n,m)αxαy(n⋅m)4f((t⋅m)2αx2+(b⋅m)2αy2(n⋅m))D({\bf{m}}) =\frac{\chi^+({\bf{n}},{\bf{m}})}{\alpha_x\alpha_y({\bf{n}}\cdot{\bf{m}})^4}f\left(\frac{\sqrt{\frac{({\bf{t}}\cdot{\bf{m}})^2}{\alpha_x^2}+\frac{({\bf{b}}\cdot{\bf{m}})^2}{\alpha_y^2}}}{({\bf{n}}\cdot{\bf{m}})}\right) D(m)=αx​αy​(n⋅m)4χ+(n,m)​f⎝⎛​(n⋅m)αx2​(t⋅m)2​+αy2​(b⋅m)2​​​⎠⎞​

相应的遮挡项GGG,只需要改变Λ(s)\Lambda({\bf{s}})Λ(s)中的变量ccc:

c=n⋅sαx2(t⋅s)2+αy2(b⋅s)2c = \frac{{\bf{n}}\cdot{\bf{s}}}{\sqrt{\alpha_x^2({\bf{t}}\cdot{\bf{s}})^2+\alpha_y^2({\bf{b}}\cdot{\bf{s}})^2}} c=αx2​(t⋅s)2+αy2​(b⋅s)2​n⋅s​

这里的s{\bf{s}}s可以是入射光线l{\bf{l}}l,也可以是出射光线v{\bf{v}}v。

有了一般的推广形式,我们将其应用到具有形状不变性的Beckmann和GGX模型上,有:

DBeckmann(h,αx,αy)=χ+(n,h)παxαy(n⋅h)4exp⁡(−(t⋅h)2αx2+(b⋅h)2αy2(n⋅h)2)DGGX(h,αx,αy)=χ+(n,h)παxαy((t⋅h)2αx2+(b⋅h)2αy2+(n⋅h)2)2\begin{aligned} D_{Beckmann}({\bf{h}}, \alpha_x, \alpha_y) & = \frac{\chi^+({\bf{n}},{\bf{h}})}{\pi\alpha_x\alpha_y({\bf{n}}\cdot{\bf{h}})^4}\exp\left(-{\frac{\frac{({\bf{t}}\cdot{\bf{h}})^2}{\alpha_x^2}+\frac{({\bf{b}}\cdot{\bf{h}})^2}{\alpha_y^2}}{({\bf{n}}\cdot{\bf{h}})^2}}\right) \\ D_{GGX}({\bf{h}}, \alpha_x, \alpha_y) & = \frac{\chi^+({\bf{n}},{\bf{h}})}{\pi\alpha_x\alpha_y\left(\frac{({\bf{t}}\cdot{\bf{h}})^2}{\alpha_x^2}+\frac{({\bf{b}}\cdot{\bf{h}})^2}{\alpha_y^2}+({\bf{n}}\cdot{\bf{h}})^2\right)^2} \end{aligned} DBeckmann​(h,αx​,αy​)DGGX​(h,αx​,αy​)​=παx​αy​(n⋅h)4χ+(n,h)​exp⎝⎛​−(n⋅h)2αx2​(t⋅h)2​+αy2​(b⋅h)2​​⎠⎞​=παx​αy​(αx2​(t⋅h)2​+αy2​(b⋅h)2​+(n⋅h)2)2χ+(n,h)​​

Clear Coat

Clear Coat模型是多层材质的一种最简单的形式。Clear Coat的中文一般翻译为“清漆”,但是这个中文词汇跟英文词汇一样让人摸不着头脑。简单来说,Clear Coat指的是材质表面的一层薄薄的透明薄膜,比如木制家具表面刷的漆,汽车的喷漆等等。Clear Coat Layer一般是透明的,光线会透过Clear Coat Layer照射到Base Layer,为了模拟Clear Coat材质,一般将这两层材质按照下图的方式建模:

Clear Coat Layer会产生Specular反射,剩余的光线照射到Base Layer产生Specular和Diffuse反射。因此,相比于基础BRDF模型,Clear Coat模型多一个由Clear Coat Layer产生的specular项。

f=Cdiffuse⋅fd+Cspecular⋅fs+Cclearcoat⋅fscf = C_{diffuse} \cdot f_d + C_{specular} \cdot f_s + C_{clearcoat} \cdot f_{sc} f=Cdiffuse​⋅fd​+Cspecular​⋅fs​+Cclearcoat​⋅fsc​

三个系数CxC_xCx​是为了保证能量守恒,不同的引擎会选取不同的计算方法。clear coat layer的specular项fscf_{sc}fsc​的计算公式与fsf_sfs​类似,也是DGF三项乘积的形式。

如果要表示一个Clear Coat模型,需要两个额外的参数,一个是ClearCoat系数,另一个是ClearCoatRoughness,指明Clear Coat Layer的粗糙度。

Subsurface

Subsurface模型也是一种多层材质,它可以很复杂(如用于绘制皮肤的SSSS模型),也可以很简单(如Disney在Principled BRDF中的实现)。

Subsurface模型描述的是名为次表面散射(subsurface scattering)的物理现象。次表面散射现象指的是光线进入材质内部,经过多次反射、吸收,最终折射出材质表面的现象。这个物理现象在BRDF模型中我们是用Diffuse分量来描述的,但是无论是Lambert模型还是Burley模型,都无法表现材质对光线的吸收作用,或者说,无法表现出“通透”效果。例如玉或者皮肤在强光照射下呈现的半透现象,或者普通光照下的通透感。下图[2]展示了采用Subsurface scattering模型的皮肤(右上)和未采用(右下)的对比。

但是完整实现subsurface scattering太复杂了,也没有必要。因此一般的subsurface模型都是通过添加一层额外的diffuse layer来近似表现次表面散射现象。这一点刚好跟Clear Coat模型相反——Clear Coat模型添加了额外的specular层,而subsurface添加了额外的diffuse层。

f=fd+fs+Csubsurface⋅fscf = f_d + f_s + C_{subsurface} \cdot f_{sc} f=fd​+fs​+Csubsurface​⋅fsc​

Cloth

布料模型和其他几个shading model很不一样,这是因为布料的组成形式是纤维相互堆叠,纤维之间还存在空隙,不符合微表面理论。相应的,Cloth model的公式也跟基于微表面理论shading model不太一样。模拟Cloth的BRDF模型大致可以分为三类:

  • 基于观察的empirical models
  • 基于微表面理论的模型
  • 微圆柱体模型(micro-cylinder model)

第一种基于观察的模型很大程度上靠艺术家调整参数来模拟布料效果,涉及到的参数比较多。第三种微圆柱体模型其实类似于头发模型——都是用圆柱体对材质进行建模,效果相对精准,但是也更繁琐。这里主要介绍第二种基于微表面理论的模型,它是通过选取合适的法向分布函数和遮挡函数来模拟布料效果。

常用的布料模型中的法向分布函数是Ashikhmin[3]提出的,用逆高斯分布来拟合法向分布,经过后续的补充,最终的形态为:

DAshikhmin(m)=χ+(n⋅m)π(1+kampα2)(1+kampexp⁡((n⋅m)2α2((n⋅m)2−1))(1−(n⋅m)2)2)GCloth(l,v)=14(n⋅v+n⋅l−(n⋅v)(n⋅l))f(l,v,h)=(1−F(h,l))ρssπ+F(h,l)D(h)G(l,v)\begin{aligned} D_{Ashikhmin}({\bf{m}}) & = \frac{\chi^+({\bf{n}}\cdot{\bf{m}})}{\pi(1+k_{amp}\alpha^2)}\left(1+\frac{k_{amp}\exp\left(\frac{({\bf{n}}\cdot{\bf{m}})^2}{\alpha^2\left(({\bf{n}}\cdot{\bf{m}})^2-1\right)}\right)}{\left(1-({\bf{n}}\cdot{\bf{m}})^2\right)^2}\right) \\ G_{Cloth}({\bf{l}},{\bf{v}}) & =\frac{1}{4({\bf{n}}\cdot{\bf{v}}+{\bf{n}}\cdot{\bf{l}}-({\bf{n}}\cdot{\bf{v}})({\bf{n}}\cdot{\bf{l}}))} \\ f({\bf{l}},{\bf{v}},{\bf{h}}) & = (1-F({\bf{h}},{\bf{l}}))\frac{\rho_{ss}}{\pi} + F({\bf{h}},{\bf{l}})D({\bf{h}})G({\bf{l}},{\bf{v}}) \end{aligned} DAshikhmin​(m)GCloth​(l,v)f(l,v,h)​=π(1+kamp​α2)χ+(n⋅m)​⎝⎛​1+(1−(n⋅m)2)2kamp​exp(α2((n⋅m)2−1)(n⋅m)2​)​⎠⎞​=4(n⋅v+n⋅l−(n⋅v)(n⋅l))1​=(1−F(h,l))πρss​​+F(h,l)D(h)G(l,v)​

其中,α\alphaα控制逆高斯函数的宽度,kampk_{amp}kamp​控制幅度。f(l,v,h)f({\bf{l}},{\bf{v}},{\bf{h}})f(l,v,h)已经包含了diffuse和specular两部分。

float D_Ashikhmin(float roughness, float NoH) {// Ashikhmin 2007, "Distribution-based BRDFs"float a2 = roughness * roughness;float cos2h = NoH * NoH;float sin2h = max(1.0 - cos2h, 0.0078125); // 2^(-14/2), so sin2h^2 > 0 in fp16float sin4h = sin2h * sin2h;float cot2 = -cos2h / (a2 * sin2h);return 1.0 / (PI * (4.0 * a2 + 1.0) * sin4h) * (4.0 * exp(cot2) + sin4h);
}

另一种布料模型是Estevez和Kulla提出的Charlie模型,Charlie模型采用的是正弦函数的幂来拟合,而不是逆高斯函数。它的实现更简单、外观更柔和。

DCharlie=(2+1α)sin⁡1αθ2πD_{Charlie} = \frac{(2+\frac{1}{\alpha})\sin^{\frac{1}{\alpha}}\theta}{2\pi} DCharlie​=2π(2+α1​)sinα1​θ​

实现:

float D_Charlie(float roughness, float NoH) {// Estevez and Kulla 2017, "Production Friendly Microfacet Sheen BRDF"float invAlpha  = 1.0 / roughness;float cos2h = NoH * NoH;float sin2h = max(1.0 - cos2h, 0.0078125); // 2^(-14/2), so sin2h^2 > 0 in fp16return (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI);
}

着色模型简介和实现(上)相关推荐

  1. 着色模型简介和实现(下)

    接上文,本文主要介绍UE4.Filament以及Disney对前面介绍的几种shading model的实现. 实现 着色模型(Shading model)指的是材质如何对入射光线做出反应,可以理解为 ...

  2. 虚幻引擎之自定义着色模型(ShadingModel)

    虚幻引擎之自定义着色模型(ShadingModel) 文章目录 虚幻引擎之自定义着色模型(ShadingModel) 一.前言 二.着色模型 2.1 材质输入 2.2 模型浅析 2.2.1 Defau ...

  3. 通俗易懂的Go协程的引入及GMP模型简介

    本文根据Golang深入理解GPM模型加之自己的理解整理而来 Go协程的引入及GMP模型 一.协程的由来 1. 单进程操作系统 2. 多线程/多进程操作系统 3. 引入协程 二.golang对协程的处 ...

  4. 主题模型简介(Topic Models)

    主题模型简介(Topic Models) 要想更好地管理当今爆炸式的电子文档档案,需要使用新的技术或工具来处理自动组织.搜索.索引和浏览大型电子文档集合.在当今机器学习和统计学研究的基础上,利用层次概 ...

  5. 点击率预估的几个经典模型简介

     点击率预估的几个经典模型简介 2016-02-22 00:32 点击率预估是大数据技术应用的最经典问题之一,在计算广告,推荐系统,金融征信等等很多领域拥有广泛的应用.本文不打算对这个话题做个全面 ...

  6. SNF快速开发平台2019-权限管理模型简介-权限都在这里

    1.1    权限的概念 权限是指为了保证职责的有效履行,任职者必须具备的,对某事项进行决策的范围和程度.它常常用"具有批准--事项的权限"来进行表达.例如,具有批准预算外5000 ...

  7. bert模型简介、transformers中bert模型源码阅读、分类任务实战和难点总结

    bert模型简介.transformers中bert模型源码阅读.分类任务实战和难点总结:https://blog.csdn.net/HUSTHY/article/details/105882989 ...

  8. ASP.NET中Session模型简介

    阅读本文章前,需要读者对以下知识有所了解.否则,阅读过程中会在相应的内容上遇到不同程度的问题. 懂得ASP/ASP.NET编程 了解ASP/ASP.NET的Session模型 了解ASP.NET We ...

  9. 门槛回归模型_门限回归汇总与空间门槛回归模型简介

    来源 | 数量经济学综合整理 转载请联系 进行回归分析,一般需要研究系数的估计值是否稳定.很多经济变量都存在结构突变问题,使用普通回归的做法就是确定结构突变点,进行分段回归.这就像我们高中学习的分段函 ...

最新文章

  1. 如何像用MNIST一样来用ImageNet?这里有一份加速TensorFlow分布式训练的梯度压缩指南
  2. HTML5 视频转换软件 Freemake Video Converter
  3. ES6 WeakMap的实际用途是什么?
  4. Keepalived — VRRP 的 Linux 软件实现
  5. 自学python有用么_Python自学可以吗?
  6. 用 Span 对 C# 进程中三大内存区域进行统一访问 ,太厉害了!
  7. java开发中准则怎么写_Java开发中通用的方法和准则20条
  8. 修改mslsql服务器端口号,复制服务器安装步骤.doc
  9. 【leetcode】中文版题库一刷ing
  10. 微型计算机外观分为,2015计算机应用基础单选练习题1.1
  11. 烽火路由路虚拟服务器,烽火路由器怎么设置?烽火路由器设置详解
  12. AmazeUI 图标的示例代码
  13. 单片机怎么通过按键控制计时器的开始和停止_《搞机作战室》机械师PX780控制中心软件使用教程...
  14. ubuntu 拷贝文件夹下所有文件到其他文件夹操作
  15. XP纯净版光盘ISO镜像文件
  16. Linkedin被“豪吞”,是互联网时代垄断者的网络效应
  17. 超标量处理器设计 姚永斌 第9章 指令执行 摘录
  18. 总结:LSM-Tree
  19. Microsoft Office 97(微软Office办公软件)绿色经典珍藏版 | office97中文版下载
  20. 甚么是佛法?(2) (沈家桢博士著)

热门文章

  1. 微信小程序即时聊天对话窗口静态源码
  2. 一维激波管内流动CFD实现(附C++源码)
  3. 2022 第13届 中国大学生服务外包创新创业大赛 作品提交要求
  4. STM32系列后缀的意思
  5. 500万像时代 10款热门拍照手机导购
  6. appstore上架截图和构建版本上传方法
  7. 工业4G模块的全面解读
  8. ai作文批改_AI批改作文哪家强?英语老师亲测讯飞智能学习机更高效
  9. linux终端显示长度,动态获得Linux终端的大小(尺寸)
  10. 素描对学计算机的意义,学素描有什么用?为什么学素描?