【Shader】 用SurfaceShader實(shí)現(xiàn)的NPR渲染

在URP中,SurfaceShader已經(jīng)不再被支持了,學(xué)URP和HLSL去吧沉颂,別碰SurfaceShader了。

前言

??沒(méi)錯(cuò)悦污,又是老生常談的NRP(非真實(shí))渲染铸屉,或者說(shuō)卡通渲染。最近事情不多切端,研究了一下Shader方面感興趣的東西彻坛,首先試了一下用RenderTexture實(shí)現(xiàn)的實(shí)時(shí)MatCap,有點(diǎn)意思但找不到什么應(yīng)用場(chǎng)景,暫時(shí)丟到一邊了昌屉。然后不知道為什么又回到了NPR上面钙蒙,于是就照著現(xiàn)在公司項(xiàng)目中的渲染效果為參考來(lái)編寫了。最后的結(jié)果感覺(jué)完成度還可以间驮,所以拿出來(lái)吹逼一下躬厌。

Outline 描邊

??搞NPR的要點(diǎn)之一無(wú)疑就是描邊,關(guān)于這個(gè)竞帽,有件事就想提一下扛施。我寫Shader都是用SurfaceShader編寫。之前做描邊碰到的最大的問(wèn)題就是怎么實(shí)現(xiàn)描邊屹篓,因?yàn)槟菚r(shí)候看到不止一篇文章說(shuō)SurfaceShader沒(méi)有Pass疙渣,就非常僵硬。但是堆巧,這次我在搜索的時(shí)候居然發(fā)現(xiàn)原來(lái)SurfaceShader是可以加Pass的妄荔!所以描邊問(wèn)題自然就引刃而解了,感謝這篇文章谍肤。
??做法是傳統(tǒng)的Inverted-Hull啦租,代碼是從Toony Colors Pro 2插件中抄來(lái)的,翻譯了一下變成SurfaceShader可以用的代碼谣沸。支持多種途徑(通常刷钢、頂點(diǎn)顏色、切線乳附、UV)來(lái)控制描邊内地,支持固定寬度,支持通過(guò)一些參數(shù)微調(diào)赋除,效果蠻玄學(xué)的阱缓,不過(guò)總比不能調(diào)要好。

不要用Cutout
有時(shí)制作頭發(fā)或者衣服上有鏤空之類举农,會(huì)采用Cutout的做法荆针,在Shader里使用clip函數(shù)對(duì)像素進(jìn)行剔除。但是想要使用Inverted-Hull來(lái)做描邊的話颁糟,這種做法就不行了航背,因?yàn)槊柽吺歉鴐esh走的,剔除掉表面的像素并不會(huì)改變mesh的形狀棱貌,描邊就會(huì)出現(xiàn)問(wèn)題玖媚。而且面片對(duì)這種描邊方式本身就不友好,所以不要再用面片做頭發(fā)了婚脱,請(qǐng)做出體積今魔!

自定義光照:二刺螈

??二刺螈不需要漸變勺像!一般的Lambert模型的光照計(jì)算公式為dot(normal, lightDir) * atten,最簡(jiǎn)單的做法——對(duì)其round一下就可以使明暗分離為兩層了错森。

??某些情況下吟宦,僅僅兩層的明暗關(guān)系可能不夠用,所以使Shader還支持了Ramp貼圖來(lái)對(duì)光照進(jìn)行映射涩维⊙晷眨可以自己制作不同的Ramp貼圖來(lái)實(shí)現(xiàn)想要的效果。適當(dāng)采用一些漸變也有著反鋸齒的效果(下圖中間)激挪。
??其實(shí)這也是非吵浇疲基礎(chǔ)的操作锋叨,在Unity官方的Surface Shader Custom Lighting Example里就有示例垄分。

五彩斑斕的黑

??暗部如果只是純黑色就顯得很悶了,現(xiàn)在日系插畫都有著很漂亮的暗部顏色娃磺,所以增加了對(duì)暗部進(jìn)行著色的功能薄湿,混色算法采用了PS圖層混合模式的“濾色”模式。

Cel貼圖

??研究公司項(xiàng)目里的角色渲染時(shí)偷卧,發(fā)現(xiàn)存在一張被廣泛的使用的被稱為Cel的貼圖豺瘤,用來(lái)控制陰影形狀,有點(diǎn)法線貼圖的意思听诸。嘗試反推了該貼圖的用法坐求,使用后可以在頭發(fā)和衣服褶皺等地方看到明顯的效果。

邊緣光

這個(gè)很常見(jiàn)也很簡(jiǎn)單我就不多說(shuō)了晌梨,總之在NPR中也是蠻必要的一種效果桥嗤。

Stylized Highlight 風(fēng)格化高光

??一開始用傳統(tǒng)Blinn-Phong模型的高光算法,效果相當(dāng)惡心仔蝌,所幸找到了一個(gè)好用的輪子——風(fēng)格化的高光泛领。代碼是從這里來(lái)的,我翻譯了一下敛惊,然后添加了一個(gè)SpecMask貼圖的功能——對(duì)于不需要顯示高光的區(qū)域涂黑即可渊鞋。
??友情提示:此效果不適合面數(shù)很低的模型。

關(guān)于反鋸齒

??由于有外描邊這種細(xì)線的存在瞧挤,不進(jìn)行反鋸齒就很容易滿屏幕狗牙锡宋,分辨率越低越明顯,所以極力推薦采取一定的反鋸齒措施特恬。不管是MSAA還是后期處理的TAA或FXAA(在官方的PostProcessing包中就有)执俩,都會(huì)讓畫面觀感明顯變好,順便再配合一些此類渲染必備的Bloom效果鸵鸥,就可以獲得比較滿意的畫面了奠滑。

關(guān)于打光

??和一般的實(shí)時(shí)光照打光方式相同丹皱,推薦一個(gè)Directional Light即可。此外也會(huì)受環(huán)境光(Environment Lighting)影響宋税, 可以在Lighting頁(yè)面里調(diào)整摊崭。

完整代碼

特性大致就是以上這些了,下面是完整的Shader代碼杰赛。

// ----------一些參考----------
// http://www.ggxrd.com/Motomura_Junya_GuiltyGearXrd.pdf
// ----------------------------
Shader "Gypsum/Cel-Shading" {
    Properties {
        [Header(Culling)]
        [Enum(UnityEngine.Rendering.CullMode)] _Cull ("Cull Mode", Float) = 2
        
        [Space(5)]
        [Header(Base Color)]
        _Color ("Tint", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}

        [Header(Cel Shading Parameters)]
        _ShadowColor ("Shadow Color", Color) = (0,0,0,1)
        [Toggle(_ENABLE_RAMP)] _EnableRamp ("Enable Ramp", float) = 0.0
        _RampTex ("Ramp Map", 2D) = "white" {}
        _CelTex ("Cel Map", 2D) = "white" {}
        _CelOffset ("Cel Offset", Range(-1,1)) = 0

        [Space(5)]
        [Header(Rim Light)]
        [HDR] _RimColor ("Rim Color", Color) = (1,1,1,1)
        _RimPower ("Rim Power", Range(1,64)) = 8

        [Space(5)]
        [Header(Outline)]
        [KeywordEnum(REGULAR,VERTEXCOLOR,TANGENT,UV2)] _OutlineNormalMode ("Normal Mode", float) = 0.0
        [Toggle(_OUTLINECONSTWIDTH)] _OutlineConstWidth ("Constant Width", float) = 0.0
        _OutlineColor ("Color", Color) = (0, 0, 0, 1)
        _OutlineWidth ("Width", Range(0,5)) = 1.0
        [Toggle(_OUTLINEZSMOOTH)] _OutlineZSmooth ("Enable Z Correction", float) = 0.0
        _ZSmooth ("Z Correction", Range(-3.0,3.0)) = -0.5
        _Offset1 ("Z Offset", Float) = 0
        // _Offset2 ("Z Offset 2", Float) = 0 //似乎沒(méi)什么作用所以沒(méi)有啟用

        [Space(5)]
        [Header(Specular)]
        [Toggle(_ENABLE_SPECULAR)] _EnableSpecular ("Enable", float) = 0.0
        [HDR] _SpecularColor ("Color", Color) = (1, 1, 1, 1)
        _SpecularMask ("Mask", 2D) = "white" {}
        _SpecularPower ("Shininess", Range(1, 100)) = 48
        _SpecularSegment ("Segment", Range(0, 1)) = 0.9
    }

    Subshader {
        Tags { "RenderType"="Opaque"}

        CGPROGRAM
        #pragma surface surf Cel addshadow
        #pragma shader_feature _ENABLE_SPECULAR
        #pragma shader_feature _ENABLE_RAMP

        sampler1D _RampTex;
        sampler2D _CelTex;
        sampler2D _MainTex;
        sampler2D _SpecularMask;

        fixed _CelOffset;
        fixed4 _ShadowColor;
        fixed4 _Color;
        fixed4 _RimColor;
        half _RimPower;
        half4 _SpecularColor;
        half _SpecularPower;
        fixed _SpecularSegment;

        // ----------一些顏色混合函數(shù)----------
        fixed Greyscale(fixed3 input)
        {
            return (input.r + input.g + input.b) / 3; 
        }
        fixed3 Blend_Multiply(fixed3 color0, fixed3 color1)
        {
            return color0 * color1;
        }
        fixed3 Blend_Overlay(fixed3 color0, fixed3 color1)
        {
            if(Greyscale(color0) <= 0.5)
            {
                return 2 * color0 * color1;
            }
            else
            {
                return 1 - 2 * ((1 - color0) * (1 - color1));
            }
        }
        fixed3 Blend_Screen(fixed3 color0, fixed3 color1)
        {
            return 1 - (1 - color0) * (1 - color1);
        }
        // ------------------------------------

        struct Input {
            float2 uv_MainTex;
            float3 viewDir;
            // fixed3 worldNormal;
            // float3 worldPos;
        };

        // 自定義一個(gè)SurfaceOutput
        struct SurfaceOutputCel
        {
            fixed3 Albedo;
            fixed3 Emission;
            float3 Normal;
            fixed Alpha;
            half2 UV; //在Lighting函數(shù)中貼圖就需要傳UV到Output中
            // fixed3 WorldNormal;
            // float3 WorldPos;
        };

        void surf(Input IN, inout SurfaceOutputCel o)
        {
            // Input to Output
            o.UV = IN.uv_MainTex;
            // o.WorldNormal = IN.worldNormal;
            // o.WorldPos = IN.worldPos;
            // Rim light
            fixed rim = dot(o.Normal, IN.viewDir);
            rim = (saturate(pow(1 - rim, _RimPower)));
            fixed3 finalRim = rim * _RimColor.rgb * _RimColor.a;
            o.Emission = finalRim;
            // Base Color
            fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        
        half4 LightingCel(SurfaceOutputCel s, half3 lightDir, half3 viewDir, half atten)
        {
            // ----------Stylized Highlights----------
            // https://github.com/candycat1992/NPR_Lab
            // ---------------------------------------
#ifdef _ENABLE_SPECULAR
            fixed3 worldNormal = normalize(s.Normal);
            fixed3 worldHalfDir = normalize(viewDir + lightDir);
            fixed spec = max(0, dot(worldNormal, worldHalfDir));
            spec = pow(spec, _SpecularPower);
            fixed w = fwidth(spec);
            if (spec < _SpecularSegment + w) {
                spec = lerp(0, _SpecularSegment, smoothstep(_SpecularSegment - w, _SpecularSegment + w, spec));
            } else {
                spec = _SpecularSegment;
            }
            half3 specular = spec * _SpecularColor.rgb * tex2D(_SpecularMask, s.UV);
#else
            fixed3 specular = 0;
#endif
            // ----------------------------------------
            // ----------Cel-Shading Lighting----------
            // ----------------------------------------
            half NdotL = dot(s.Normal, lightDir);
            half cel = lerp(fixed3(1,1,1), saturate(Greyscale(tex2D(_CelTex, s.UV) + _CelOffset)), dot(lightDir,s.Normal));
#ifdef _ENABLE_RAMP
            cel = tex1D(_RampTex, cel);
            half ramp = tex1D(_RampTex, saturate(atten * NdotL) * 0.5 + 0.5);
            half3 shadow = lerp(fixed3(1,1,1), Blend_Screen(fixed3(1,1,1) * saturate(ramp * cel), _ShadowColor.rgb), _ShadowColor.a);
#else
            half3 shadow = lerp(fixed3(1,1,1), Blend_Screen(fixed3(1,1,1) * saturate(round(NdotL * atten * cel)), _ShadowColor.rgb), _ShadowColor.a);
#endif
            half4 c;
            c.rgb = Blend_Screen(shadow * s.Albedo * _LightColor0, specular);
            c.a = s.Alpha;
            return c;
        }
        ENDCG
        // ----------Outline Pass----------
        // https://www.videopoetics.com/tutorials/pixel-perfect-outline-shaders-unity/#building-the-classic-outline-shader
        // https://assetstore.unity.com/packages/vfx/shaders/toony-colors-pro-2-8105
        // --------------------------------
        Pass {
            Cull Front
            Offset [_Offset1], 0 //[_Offset2]
            
            CGPROGRAM
            #include "UnityCG.cginc"
            #pragma multi_compile _OUTLINENORMALMODE_REGULAR _OUTLINENORMALMODE_VERTEXCOLOR _OUTLINENORMALMODE_TANGENT _OUTLINENORMALMODE_UV2
            #pragma shader_feature _OUTLINECONSTWIDTH
            #pragma shader_feature _OUTLINEZSMOOTH
            #pragma vertex Vertex
            #pragma fragment Fragment

            half _ZSmooth;
            half _OutlineWidth;
            half4 _OutlineColor;

            struct a2v
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            }; 
            
            struct v2f
            {
                float4 pos : SV_POSITION;
            };

            v2f Vertex(a2v v)
            {
                v2f o;
                UNITY_SETUP_INSTANCE_ID(v);

                //Correct Z artefacts
                #ifdef _OUTLINEZSMOOTH
                    float4 pos = float4(UnityObjectToViewPos(v.vertex), 1.0);
                    
                    #ifdef _OUTLINENORMALMODE_VERTEXCOLOR
                        //Vertex Color for Normals
                        float3 normal = mul((float3x3)UNITY_MATRIX_IT_MV, (v.color.xyz*2) - 1);
                    #elif _OUTLINENORMALMODE_TANGENT
                        //Tangent for Normals
                        float3 normal = mul((float3x3)UNITY_MATRIX_IT_MV, v.tangent.xyz);
                    #elif _OUTLINENORMALMODE_UV2
                        //UV2 for Normals
                        float3 normal;
                        //unpack uv2
                        v.uv2.x = v.uv2.x * 255.0/16.0;
                        normal.x = floor(v.uv2.x) / 15.0;
                        normal.y = frac(v.uv2.x) * 16.0 / 15.0;
                        //get z
                        normal.z = v.uv2.y;
                        //transform
                        normal = mul( (float3x3)UNITY_MATRIX_IT_MV, normal*2-1);
                    #else
                        float3 normal = mul( (float3x3)UNITY_MATRIX_IT_MV, v.normal);
                    #endif
                    
                    normal.z = -_ZSmooth;
                    
                    #ifdef _OUTLINECONSTWIDTH
                        //Camera-independent outline size
                        float dist = distance(_WorldSpaceCameraPos, mul(unity_ObjectToWorld, v.vertex));
                        pos = pos + float4(normalize(normal),0) * _OutlineWidth * 0.01 * dist;
                    #else
                        pos = pos + float4(normalize(normal),0) * _OutlineWidth * 0.01;
                    #endif
                    
                #else

                    #ifdef _OUTLINENORMALMODE_VERTEXCOLOR
                        //Vertex Color for Normals
                        float3 normal = (v.color.xyz*2) - 1;
                    #elif _OUTLINENORMALMODE_TANGENT
                        //Tangent for Normals
                        float3 normal = v.tangent.xyz;
                    #elif _OUTLINENORMALMODE_UV2
                        //UV2 for Normals
                        float3 n;
                        //unpack uv2
                        v.uv2.x = v.uv2.x * 255.0/16.0;
                        n.x = floor(v.uv2.x) / 15.0;
                        n.y = frac(v.uv2.x) * 16.0 / 15.0;
                        //get z
                        n.z = v.uv2.y;
                        //transform
                        n = n*2 - 1;
                        float3 normal = n;
                    #else
                        float3 normal = v.normal;
                    #endif
                    
                    //Camera-independent outline size
                    #ifdef _OUTLINECONSTWIDTH
                        float dist = distance(_WorldSpaceCameraPos, mul(unity_ObjectToWorld, v.vertex));
                        float4 pos =  float4(UnityObjectToViewPos(v.vertex + float4(normal, 0) * _OutlineWidth * 0.01 * dist), 1.0);
                    #else
                        float4 pos = float4(UnityObjectToViewPos(v.vertex + float4(normal, 0) * _OutlineWidth * 0.01), 1.0);
                    #endif
                #endif
                o.pos = mul(UNITY_MATRIX_P, pos);
                return o;
            }
            
            float4 Fragment (v2f IN) : COLOR
            {
                return _OutlineColor;
            }
            ENDCG
        }
    }
}

結(jié)語(yǔ)

??之前第一次看《罪惡裝備Xrd》藝術(shù)風(fēng)格講解的時(shí)候呢簸,有種驚為天人的感覺(jué),就一直想著自己什么時(shí)候也可以試著搞一下這類Shader乏屯。其實(shí)NPR都是一些很老的技術(shù)根时,好幾年前就可以做到了,只是它的難點(diǎn)從來(lái)就不是技術(shù)辰晕。
??PPT里有幾個(gè)點(diǎn)我認(rèn)為講得非常好:

  • 不只是一個(gè)Shader就完事蛤迎,而是要構(gòu)建整個(gè)工作流。(Not just a shader, but a whole workflow)
    ??他們的人物動(dòng)畫每一幀都是手K含友,而且不做補(bǔ)間替裆,是為了追求“有限動(dòng)畫”的感覺(jué)。并且動(dòng)畫的每一幀都會(huì)對(duì)光照做針對(duì)性調(diào)整窘问,還充斥著大量的形變縮放辆童,以營(yíng)造日式動(dòng)畫類似“金田系”作畫的夸張透視效果。最后游戲能呈現(xiàn)出這樣幾乎沒(méi)有破綻的2D效果惠赫,巨大的美術(shù)工作量的功不可沒(méi)把鉴。試圖用一個(gè)Shader就想讓自己的游戲達(dá)到完美的風(fēng)格化渲染效果,無(wú)疑是天真的儿咱。

  • 讓美術(shù)決定效果庭砍,而不是數(shù)學(xué)公式。(Let the artist decide, not the math)
    ??確實(shí)有時(shí)候就會(huì)碰到這種情況——這個(gè)公式看起來(lái)更正確一點(diǎn)概疆,但是效果很微妙逗威;那種算法看起來(lái)很莫名其妙,但是效果很棒岔冀。所以該用哪種凯旭?可能大部分時(shí)候我們只需要表象正確就可以了,畢竟做游戲就少不了Trick使套,沒(méi)必要一味的追求“正確”吧罐呼。

??以上是一些個(gè)人的小小感想。希望本文對(duì)你有用侦高,再見(jiàn)嫉柴。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市奉呛,隨后出現(xiàn)的幾起案子计螺,更是在濱河造成了極大的恐慌夯尽,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件登馒,死亡現(xiàn)場(chǎng)離奇詭異匙握,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)陈轿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門圈纺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人麦射,你說(shuō)我怎么就攤上這事蛾娶。” “怎么了潜秋?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵蛔琅,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我半等,道長(zhǎng)揍愁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任杀饵,我火速辦了婚禮,結(jié)果婚禮上谬擦,老公的妹妹穿的比我還像新娘切距。我一直安慰自己,他們只是感情好惨远,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布谜悟。 她就那樣靜靜地躺著,像睡著了一般北秽。 火紅的嫁衣襯著肌膚如雪葡幸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天贺氓,我揣著相機(jī)與錄音蔚叨,去河邊找鬼。 笑死辙培,一個(gè)胖子當(dāng)著我的面吹牛蔑水,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播扬蕊,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼搀别,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了尾抑?” 一聲冷哼從身側(cè)響起歇父,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蒂培,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后榜苫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體毁渗,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年单刁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了灸异。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡羔飞,死狀恐怖肺樟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情逻淌,我是刑警寧澤么伯,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站卡儒,受9級(jí)特大地震影響田柔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜骨望,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一硬爆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧擎鸠,春花似錦缀磕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至绢涡,卻和暖如春牲剃,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背雄可。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工凿傅, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人滞项。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓狭归,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親文判。 傳聞我的和親對(duì)象是個(gè)殘疾皇子过椎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 今天四嫂都六十六了 老陳家四十多口人 都在祝福 四嫂 漂亮善良厚重 西山坡造就了 美麗善良的姑娘 最初的美我們懂得...
    云中漫步無(wú)窮盡閱讀 279評(píng)論 0 4
  • 越是無(wú)能的人。 越是喜歡坑害對(duì)他好的人戏仓。 坑的他周圍的人傷心的離開疚宇。 最后亡鼠,他 周圍就剩下跟他一樣垃圾的人。 越是...
    2cf888c015e6閱讀 246評(píng)論 0 0
  • 無(wú)所事事敷待,電腦卡间涵,又不想開電視,想表達(dá)點(diǎn)什么榜揖?又怕暴露些什么真的有點(diǎn)像當(dāng)初 少年不愁滋味勾哩,為賦新詞強(qiáng)說(shuō)愁的時(shí)節(jié),有...
    章渺渺閱讀 172評(píng)論 1 0
  • 1.背景:作為一名測(cè)試举哟,在沒(méi)有apk的情況下如何測(cè)試移動(dòng)端的功能呢思劳,拉取Git代碼,本地啟服務(wù)妨猩,在自己本機(jī)測(cè)試 2...
    軟件測(cè)試筆記閱讀 2,230評(píng)論 0 1