本文介绍了了解OpenGL中的照明的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 我试图用Haskell / GLUT从一堆三角形中创建3D球体。它工作得非常好:绿色的是我的球体,红色的是GLUT的renderObject Sphere。当我移动相机时,我可以看到我的球体真的是3D,所以没关系。 那么为什么GLUT的灯具有很好的照明,而我的灯具却没有? (我是一个新手,并不真正知道我在initGL下面做了什么,从Hackage的cuboid包中复制了这些东西......) 以下是代码: 模块Main其中 import Graphics.UI.GLUT main: :IO() main = do initGL displayCallback $ = render mainLoop initGL :: IO() initGL = do getArgsAndInitialize initialDisplayMode $ = [DoubleBuffered] createWindowChip! initialDisplayMode $ = [WithDepthBuffer] depthFunc $ =只是减 clearColor $ = Color4 0 0 0 0 light(Light 0)$ = Enabled lighting $ =已启用 lightModelAmbient $ = Color4 0.5 0.5 0.5 1 diffuse(Light 0)$ = Color4 1 1 1 1 blend $ =已启用 blendFunc $ =(SrcAlpha,OneMinusSrcAlpha) colorMaterial $ = Just(FrontAndBack,AmbientAndDiffuse) reshapeCallback $ =只是resizeScene return() $ b $ render:DisplayCallback render = do 清除[ColorBuffer,DepthBuffer] loadIdentity color $ Color3(1 :: GLdouble)1 1 position(Light 0)$ = Vertex4 0 50( 50)1 preservingMatrix $ do translate $ Vector3((-0.5):: GLfloat)(-0.5)(-5)绿色球12 8 0.03 preservingMatrix $ do 翻译$ Vector3(0.5 :: GLfloat)0.5(-5) color red renderObject Solid(Sphere'0.25 20 20) flush swapBuffers 其中green = Color4 0.8 1.0 0.7 0.9 :: Color4 GLdouble red = Color4 1.0 0.7 0.8 1.0 :: Color4 GLdouble vertex3f ::(GLfloat,GLfloat,GLfloat) - > IO() vertex3f(x,y,z)=顶点$顶点3 x y z upperInnerCircle :: Int - > [(GLfloat,GLfloat)] upperInnerCircle numSegs = concat [[(0,0),(cos a,sqrt(1-(cos a)*(cos a))) ,(cos b,sqrt(1-(cos b)*(cos b)))] | (a,b) 其中 seg'= pi /(来自整数numSegs) as = [(来自整数n * seg',来自整数(n + 1)* seg ')| n lowerInnerCircle :: Int - > [(GLfloat,GLfloat)] lowerInnerCircle numSegs = map(\(x,y) - >(x,-y))$ upperInnerCircle numSegs innerCircle: :Int - > [(GLfloat,GLfloat)] innerCircle numSegs = upperInnerCircle numSegs ++(lowerInnerCircle numSegs) upperOutSegment :: Int - > Int - > Int - > [(GLfloat,GLfloat)] upperOutSegment numSegs ring seg = [x,y,u,u,v,y] 其中 seg'= pi /(fromIntegral numSegs )(a,b)=(fromIntegral seg * seg',fromIntegral(seg + 1)* seg')x =(fromIntegral ring * cos a,fromIntegral ring * sqrt(1- )*(cos a)))y =(fromIntegral ring * cos b,fromIntegral ring * sqrt(1-(cos b)*(cos b)))u =(fromIntegral(ring + 1) * cos a,从积分(环+ 1)* sqrt(1-(cos a)*(cos a)))v =(从积分(环+ 1)* cos b, (1-(cos b)*(cos b))) lowerOutSegment :: Int - > Int - > Int - > [(GLfloat,GLfloat)] lowerOutSegment numSegs ring seg = map(\(x,y) - >(x,-y))$ upperOutSegment numSegs ring seg outSegment :: Int - > Int - > Int - > [(GLfloat,GLfloat)] outSegment numSegs ring seg = upperOutSegment numSegs ring seg ++(lowerOutSegment numSegs ring seg) outerRing :: Int - > Int - > [(GLfloat,GLfloat)] outerRing numSegs ring = concat [outSegment numSegs ring n | n ball numSegs numRings factor = let ips = innerCircle numSegs ops = concat [outerRing numSegs i | i< - [1..numRings]] height dir ps = map(\(x,y) - > let dist = sqrt(x * x + y * y )/(fromIntegral(numRings + 1)) height'= sqrt(1.001-dist * dist)* factor *(fromIntegral(numRings + 1)) in(x * factor,y * factor, dir * height'))$ ps ups = height 1 $ ips ++ ops lps = height(-1)$ ips ++ ops in renderPrimitive Triangles $ mapM_ vertex3f(ups + + lps) resizeScene :: Size - > IO() resizeScene(Size w 0)= resizeScene(Size w 1) - 防止除以零 resizeScene s @(Size width height)= do viewport $ =(位置0 0,s) matrixMode $ = Projection loadIdentity perspective 45(w2 / h2)1 1000 matrixMode $ = Modelview 0 flush 其中 w2 =半宽b $ b h2 =一半身高一半z = realToFrac z / 2 编辑:现在有效,感谢Spektre! 以下是图片: 以下是代码: 模块Main其中 import Graphics.UI.GLUT main :: IO( ) main = do initGL displayCallback $ = render mainLoop initGL :: IO() initGL = do getArgsAndInitialize initialDisplayMode $ = [DoubleBuffered] createWindowChip! initialDisplayMode $ = [WithDepthBuffer] depthFunc $ =只是减 clearColor $ = Color4 0 0 0 0 light(Light 0)$ = Enabled lighting $ =已启用 lightModelAmbient $ = Color4 0.5 0.5 0.5 1 diffuse(Light 0)$ = Color4 1 1 1 1 blend $ =已启用 blendFunc $ =(SrcAlpha,OneMinusSrcAlpha) colorMaterial $ = Just(FrontAndBack,AmbientAndDiffuse) reshapeCallback $ =只是resizeScene return() $ b $ render:DisplayCallback render = do 清除[ColorBuffer,DepthBuffer] loadIdentity color $ Color3(1 :: GLdouble)1 1 position(Light 0)$ = Vertex4 0 50( 50)1 preservingMatrix $ do translate $ Vector3((-0.5):: GLfloat)(-0.5)(-5)绿色球12 8 0.03 preservingMatrix $ do 翻译$ Vector3(0.5 :: GLfloat)0.5(-5) color red renderObject Solid(Sphere'0.25 20 20) flush swapBuffers 其中green = Color4 0.8 1.0 0.7 0.9 :: Color4 GLdouble red = Color4 1.0 0.7 0.8 1.0 :: Color4 GLdouble pushTriangle ::((GLfloat,GLfloat,GLfloat) ,(GLfloat,GLfloat,GLfloat),(GLfloat,GLfloat,GLfloat)) - > IO() pushTriangle(p0,p1,p2)= do let(_,d0,_)= p0 let(_,d1,_)= p1 let(_,d2,_)= p2 - 如果它向上指向,反向正常 let d = if d0 + d1 + d2> 0 then(-1) (n1,n2,n3)= scaleVec n(nL * d)其他1 let n = cross(减p1 p0)(减p2 p1)令nL = 1 / lenVec n let ) normal $ Normal3 n1 n2 n3 vertex3f p0 vertex3f p1 vertex3f p2 vertex3f ::(GLfloat,GLfloat,GLfloat ) - > IO() vertex3f(x,y,z)= 顶点$顶点3 xyz lenVec(a1,a2,a3)= sqrt $ a1 * a1 + a2 * a2 + a3 * a3 scaleVec(a1,a2,a3)x =(a1 * x,a2 * x,a3 * x) 交叉(a1,a2,a3 )(b1,b2,b3)= (a2 * b3-a3 * b2 ,a3 * b1-a1 * b3 ,a1 * b2-a2 * b1) 减(a1,a2,a3)(b1,b2,b3)= (a1-b1,a2-b2,a3-b3) upperInnerCircle :: Int - > [((GLfloat,GLfloat)] upperInnerCircle numSegs = concat [[(cos a,sqrt(1-(cos a)*(cos a))),(0,0) ,(cos b,sqrt(1-(cos b)*(cos b)))] | (a,b) 其中 seg'= pi /(来自整数numSegs) as = [(来自整数n * seg',来自整数(n + 1)* seg ')| n lowerInnerCircle :: Int - > [(GLfloat,GLfloat)] lowerInnerCircle numSegs = map(\(x,y) - >(x,-y))$ upperInnerCircle numSegs innerCircle: :Int - > [(GLfloat,GLfloat)] innerCircle numSegs = upperInnerCircle numSegs ++(lowerInnerCircle numSegs) upperOutSegment :: Int - > Int - > Int - > [(GLfloat,GLfloat)] upperOutSegment numSegs ring seg = [x,y,u,v,u,y] where seg'= pi /(fromIntegral numSegs )(a,b)=(fromIntegral seg * seg',fromIntegral(seg + 1)* seg')x =(fromIntegral ring * cos a,fromIntegral ring * sqrt(1- )*(cos a)))y =(fromIntegral ring * cos b,fromIntegral ring * sqrt(1-(cos b)*(cos b)))u =(fromIntegral(ring + 1) * cos a,从积分(环+ 1)* sqrt(1-(cos a)*(cos a)))v =(从积分(环+ 1)* cos b, (1-(cos b)*(cos b))) lowerOutSegment :: Int - > Int - > Int - > [(GLfloat,GLfloat)] lowerOutSegment numSegs ring seg = map(\(x,y) - >(x,-y))$ upperOutSegment numSegs ring seg outSegment :: Int - > Int - > Int - > [(GLfloat,GLfloat)] outSegment numSegs ring seg = upperOutSegment numSegs ring seg ++(lowerOutSegment numSegs ring seg) outerRing :: Int - > Int - > [(GLfloat,GLfloat)] outerRing numSegs ring = concat [outSegment numSegs ring n | n ball numSegs numRings factor = let ips = innerCircle numSegs ops = concat [outerRing numSegs i | i< - [1..numRings]] height dir ps = map(\(x,y) - > let dist = sqrt(x * x + y * y )/(fromIntegral(numRings + 1)) height'= sqrt(1.001-dist * dist)* factor *(fromIntegral(numRings + 1)) in(x * factor,y * factor, dir * height'))$ ps ups = height 1 $ ips ++ ops lps = height(-1)$ ips ++ ops in renderPrimitive Triangles $ mapM_ pushTriangle(toTriples( ups ++ lps)) toTriples :: [a] - > (a,b,c):toTriples休息 resizeScene :: Size - > IO() resizeScene(Size w 0)= resizeScene(Size w 1) - 防止除以零 resizeScene s @(Size width height)= do viewport $ =(位置0 0,s) matrixMode $ = Projection loadIdentity perspective 45(w2 / h2)1 1000 matrixMode $ = Modelview 0 flush 其中 w2 =半宽b $ b h2 =半身高半z = realToFrac z / 2 $ b $ b 垂直于表面的矢量是垂直于表面的矢量。对于三角形,通过其任意2个顶点向量的叉积来计算,所以如果三角点是 p0,p1,p2 ,那么normal是 n = cross(p1 -b0,p2-p1)或任何其他组合。 法线指示像素/面/方向是通过渲染引擎计算出来的,该引擎给出 cos(angle_between light and surface normal)。这个数字是当光源强度乘以光线颜色时光线照射到表面的比例... 与表面颜色渲染的组合获得像素颜色有许多光线模型这个是非常简单的(正常阴影)。 为了使点积工作,法线应该是单位矢量,用长度除以它的长度 n = n / | n | $ b 对于球体,法线很容易正常 n 对于任何点 p 是 n =(p-center)/ radius $ b 那么你可以做一些光线效果,如光滑的锐利网格边缘。例如在这里看看: 平滑法线 也可以达到完全相反的效果(平滑网格但尖锐边缘渲染) OpenGL接口 旧式gl使用类似于 glNormal3f(nx, ny,nz); VBO / VAO /数组也知道法线。新样式 glNormal 会像大多数参数一样被取消,因此您需要将其绑定到您自己的自定义布局中 正常方向 任何曲面都有2个可能垂直于它的垂直方向。通常使用从网格向外指向的那个。有时3D曲线是双面材料,这意味着点积被处理为 abs 值,因此法线指向的方式无关紧要。如果没有这个,表面的反面将会一直是黑暗的 p> I am trying to create a 3D sphere out of a bunch of triangles with Haskell / GLUT. It works quite nicely: The green one is "my" sphere, the red one is done with GLUT's renderObject Sphere'. And I can see "my" sphere is really 3D when I move the camera around, so that's fine. So why does the GLUT one has nice lighting, and mine has not? (I'm a newbie and do not really know what I'm doing below in initGL, copied that stuff from Hackage's cuboid package...) Here's the code:module Main whereimport Graphics.UI.GLUT main :: IO ()main = do initGL displayCallback $= render mainLoopinitGL :: IO ()initGL = do getArgsAndInitialize initialDisplayMode $= [DoubleBuffered] createWindow "Chip!" initialDisplayMode $= [ WithDepthBuffer ] depthFunc $= Just Less clearColor $= Color4 0 0 0 0 light (Light 0) $= Enabled lighting $= Enabled lightModelAmbient $= Color4 0.5 0.5 0.5 1 diffuse (Light 0) $= Color4 1 1 1 1 blend $= Enabled blendFunc $= (SrcAlpha, OneMinusSrcAlpha) colorMaterial $= Just (FrontAndBack, AmbientAndDiffuse) reshapeCallback $= Just resizeScene return () render :: DisplayCallbackrender = do clear [ ColorBuffer, DepthBuffer ] loadIdentity color $ Color3 (1 :: GLdouble) 1 1 position (Light 0) $= Vertex4 0 50 (50) 1 preservingMatrix $ do translate $ Vector3 ((-0.5) :: GLfloat) (-0.5) (-5) color green ball 12 8 0.03 preservingMatrix $ do translate $ Vector3 (0.5 :: GLfloat) 0.5 (-5) color red renderObject Solid (Sphere' 0.25 20 20) flush swapBuffers where green = Color4 0.8 1.0 0.7 0.9 :: Color4 GLdouble red = Color4 1.0 0.7 0.8 1.0 :: Color4 GLdoublevertex3f :: (GLfloat, GLfloat, GLfloat) -> IO ()vertex3f (x, y, z) = vertex $ Vertex3 x y zupperInnerCircle :: Int -> [(GLfloat, GLfloat)]upperInnerCircle numSegs = concat [[(0,0) ,(cos a, sqrt(1-(cos a)*(cos a))) ,(cos b, sqrt(1-(cos b)*(cos b)))] | (a,b)<-as ] where seg'=pi/(fromIntegral numSegs) as = [(fromIntegral n * seg', fromIntegral (n+1) * seg') | n<-[0..numSegs-1]]lowerInnerCircle :: Int -> [(GLfloat, GLfloat)]lowerInnerCircle numSegs = map (\(x,y) -> (x,-y)) $ upperInnerCircle numSegsinnerCircle :: Int -> [(GLfloat, GLfloat)]innerCircle numSegs = upperInnerCircle numSegs ++ (lowerInnerCircle numSegs)upperOutSegment :: Int -> Int -> Int -> [(GLfloat, GLfloat)]upperOutSegment numSegs ring seg = [x,y,u, u,v,y] where seg'=pi/(fromIntegral numSegs) (a, b) = (fromIntegral seg * seg', fromIntegral (seg+1) * seg') x = (fromIntegral ring * cos a, fromIntegral ring * sqrt(1-(cos a)*(cos a))) y = (fromIntegral ring * cos b, fromIntegral ring * sqrt(1-(cos b)*(cos b))) u = (fromIntegral (ring+1) * cos a, fromIntegral (ring+1) * sqrt(1-(cos a)*(cos a))) v = (fromIntegral (ring+1) * cos b, fromIntegral (ring+1) * sqrt(1-(cos b)*(cos b)))lowerOutSegment :: Int -> Int -> Int -> [(GLfloat, GLfloat)]lowerOutSegment numSegs ring seg = map (\(x,y) -> (x,-y)) $ upperOutSegment numSegs ring seg outSegment :: Int -> Int -> Int -> [(GLfloat, GLfloat)]outSegment numSegs ring seg = upperOutSegment numSegs ring seg ++ (lowerOutSegment numSegs ring seg)outerRing :: Int -> Int -> [(GLfloat, GLfloat)]outerRing numSegs ring = concat [outSegment numSegs ring n | n<-[0..numSegs-1]] ball numSegs numRings factor = let ips = innerCircle numSegs ops = concat [outerRing numSegs i | i<-[1..numRings]] height dir ps = map (\(x,y) -> let dist = sqrt(x*x+y*y)/(fromIntegral (numRings+1)) height' = sqrt(1.001-dist*dist)*factor*(fromIntegral (numRings+1)) in (x*factor,y*factor,dir*height')) $ ps ups = height 1 $ ips ++ ops lps = height (-1) $ ips ++ ops in renderPrimitive Triangles $ mapM_ vertex3f (ups++lps)resizeScene :: Size -> IO ()resizeScene (Size w 0) = resizeScene (Size w 1) -- prevent divide by zeroresizeScene s@(Size width height) = do viewport $= (Position 0 0, s) matrixMode $= Projection loadIdentity perspective 45 (w2/h2) 1 1000 matrixMode $= Modelview 0 flush where w2 = half width h2 = half height half z = realToFrac z / 2EDIT: Works now, thanks to Spektre!Here's the pic:And here's the code:module Main whereimport Graphics.UI.GLUT main :: IO ()main = do initGL displayCallback $= render mainLoopinitGL :: IO ()initGL = do getArgsAndInitialize initialDisplayMode $= [DoubleBuffered] createWindow "Chip!" initialDisplayMode $= [ WithDepthBuffer ] depthFunc $= Just Less clearColor $= Color4 0 0 0 0 light (Light 0) $= Enabled lighting $= Enabled lightModelAmbient $= Color4 0.5 0.5 0.5 1 diffuse (Light 0) $= Color4 1 1 1 1 blend $= Enabled blendFunc $= (SrcAlpha, OneMinusSrcAlpha) colorMaterial $= Just (FrontAndBack, AmbientAndDiffuse) reshapeCallback $= Just resizeScene return () render :: DisplayCallbackrender = do clear [ ColorBuffer, DepthBuffer ] loadIdentity color $ Color3 (1 :: GLdouble) 1 1 position (Light 0) $= Vertex4 0 50 (50) 1 preservingMatrix $ do translate $ Vector3 ((-0.5) :: GLfloat) (-0.5) (-5) color green ball 12 8 0.03 preservingMatrix $ do translate $ Vector3 (0.5 :: GLfloat) 0.5 (-5) color red renderObject Solid (Sphere' 0.25 20 20) flush swapBuffers where green = Color4 0.8 1.0 0.7 0.9 :: Color4 GLdouble red = Color4 1.0 0.7 0.8 1.0 :: Color4 GLdoublepushTriangle :: ((GLfloat, GLfloat, GLfloat) ,(GLfloat, GLfloat, GLfloat) ,(GLfloat, GLfloat, GLfloat)) -> IO ()pushTriangle (p0, p1, p2) = do let (_,d0,_)=p0 let (_,d1,_)=p1 let (_,d2,_)=p2 --if it points upwards, reverse normal let d=if d0+d1+d2>0 then (-1) else 1 let n = cross (minus p1 p0) (minus p2 p1) let nL = 1/lenVec n let (n1, n2, n3) = scaleVec n (nL*d) normal $ Normal3 n1 n2 n3 vertex3f p0 vertex3f p1 vertex3f p2vertex3f :: (GLfloat, GLfloat, GLfloat) -> IO ()vertex3f (x, y, z) = vertex $ Vertex3 x y zlenVec (a1,a2,a3) = sqrt $ a1*a1 + a2*a2 + a3*a3scaleVec (a1,a2,a3) x = (a1*x,a2*x,a3*x)cross (a1,a2,a3) (b1,b2,b3) = (a2*b3-a3*b2 ,a3*b1-a1*b3 ,a1*b2-a2*b1)minus (a1,a2,a3) (b1,b2,b3) = (a1-b1, a2-b2, a3-b3)upperInnerCircle :: Int -> [(GLfloat, GLfloat)]upperInnerCircle numSegs = concat [[(cos a, sqrt(1-(cos a)*(cos a))) ,(0,0) ,(cos b, sqrt(1-(cos b)*(cos b)))] | (a,b)<-as ] where seg'=pi/(fromIntegral numSegs) as = [(fromIntegral n * seg', fromIntegral (n+1) * seg') | n<-[0..numSegs-1]]lowerInnerCircle :: Int -> [(GLfloat, GLfloat)]lowerInnerCircle numSegs = map (\(x,y) -> (x,-y)) $ upperInnerCircle numSegsinnerCircle :: Int -> [(GLfloat, GLfloat)]innerCircle numSegs = upperInnerCircle numSegs ++ (lowerInnerCircle numSegs)upperOutSegment :: Int -> Int -> Int -> [(GLfloat, GLfloat)]upperOutSegment numSegs ring seg = [x,y,u, v,u,y] where seg'=pi/(fromIntegral numSegs) (a, b) = (fromIntegral seg * seg', fromIntegral (seg+1) * seg') x = (fromIntegral ring * cos a, fromIntegral ring * sqrt(1-(cos a)*(cos a))) y = (fromIntegral ring * cos b, fromIntegral ring * sqrt(1-(cos b)*(cos b))) u = (fromIntegral (ring+1) * cos a, fromIntegral (ring+1) * sqrt(1-(cos a)*(cos a))) v = (fromIntegral (ring+1) * cos b, fromIntegral (ring+1) * sqrt(1-(cos b)*(cos b)))lowerOutSegment :: Int -> Int -> Int -> [(GLfloat, GLfloat)]lowerOutSegment numSegs ring seg = map (\(x,y) -> (x,-y)) $ upperOutSegment numSegs ring seg outSegment :: Int -> Int -> Int -> [(GLfloat, GLfloat)]outSegment numSegs ring seg = upperOutSegment numSegs ring seg ++ (lowerOutSegment numSegs ring seg)outerRing :: Int -> Int -> [(GLfloat, GLfloat)]outerRing numSegs ring = concat [outSegment numSegs ring n | n<-[0..numSegs-1]] ball numSegs numRings factor = let ips = innerCircle numSegs ops = concat [outerRing numSegs i | i<-[1..numRings]] height dir ps = map (\(x,y) -> let dist = sqrt(x*x+y*y)/(fromIntegral (numRings+1)) height' = sqrt(1.001-dist*dist)*factor*(fromIntegral (numRings+1)) in (x*factor,y*factor,dir*height')) $ ps ups = height 1 $ ips ++ ops lps = height (-1) $ ips ++ ops in renderPrimitive Triangles $ mapM_ pushTriangle (toTriples (ups++lps))toTriples :: [a] -> [(a,a,a)]toTriples [] = []toTriples (a:b:c:rest) = (a,b,c):toTriples rest resizeScene :: Size -> IO ()resizeScene (Size w 0) = resizeScene (Size w 1) -- prevent divide by zeroresizeScene s@(Size width height) = do viewport $= (Position 0 0, s) matrixMode $= Projection loadIdentity perspective 45 (w2/h2) 1 1000 matrixMode $= Modelview 0 flush where w2 = half width h2 = half height half z = realToFrac z / 2 解决方案 Surface normals are crucial for lighting equationsNormal to surface is vector perpendicular to surface. For triangle is computed by cross product of any its 2 vertices vectors so if triangle points are p0,p1,p2 then normal is n=cross(p1-p0,p2-p1) or any other combination.Normals tells which way is pixel/face/polygon turned usually dot product with light direction is computed by render engine that gives a cos(angle_between light and surface normal). This number is the scale of amount of light hitting the surface when multiplied with light source strength you got the light color ...with combination of surface color render get the pixel color there are many light models this one was very simple (normal shading).To make the dot product work the normal should be unit vector so divide it by its length n=n/|n|Here small example of normalsFor sphere the normal is easy normal n for any point p is n=(p-center)/radiusIf normal does not correspond with surfacethen you can do light effects like visually smooth sharp edges of mesh. for example how Look here:smoothing normalsalso the exact opposite can be achieved (smooth mesh but sharp edge render)OpenGL interfaceold style gl uses something like glNormal3f(nx,ny,nz); The VBO/VAO/arrays knows normals too. In new style glNormal is depreceated like most parameters so you need to bind it to your custom layout on your ownNormal directionany surface has 2 possible direction of perpendicular normal to it. Usually the one pointing outwards from mesh is used. Sometimes for 3D curves is double sided material used that means that the dot product is handled as abs value so it does not matter which way the normal is pointing. Without this the opposite side of surface will be always darkSo if you have normals and no lighting is visible then try to negate normals 这篇关于了解OpenGL中的照明的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
09-18 04:28